Added texture and fixed a bunch of stuff.
This commit is contained in:
parent
9095c7e99a
commit
4ad274d637
BIN
assets/bitmaps/sdl.png
Normal file
BIN
assets/bitmaps/sdl.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 69 KiB |
21
assets/shaders/glsl/color_from_texture.frag
Normal file
21
assets/shaders/glsl/color_from_texture.frag
Normal file
@ -0,0 +1,21 @@
|
||||
#version 460
|
||||
|
||||
layout(set = 2, binding = 0)
|
||||
uniform sampler2D u_texture;
|
||||
|
||||
layout(set = 3, binding = 0)
|
||||
uniform Parameters
|
||||
{
|
||||
vec4 u_color;
|
||||
};
|
||||
|
||||
layout(location = 0)
|
||||
in vec2 i_texCoord;
|
||||
|
||||
layout(location = 0)
|
||||
out vec4 o_color;
|
||||
|
||||
void main()
|
||||
{
|
||||
o_color = texture(u_texture, i_texCoord) * u_color;
|
||||
}
|
135
private/sdl_gpu_test/1_textured_quad/app.cpp
Normal file
135
private/sdl_gpu_test/1_textured_quad/app.cpp
Normal file
@ -0,0 +1,135 @@
|
||||
|
||||
#include "./app.hpp"
|
||||
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
#include "../util/bitmap.hpp"
|
||||
|
||||
namespace sdl_gpu_test
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct Vertex
|
||||
{
|
||||
glm::vec2 pos;
|
||||
glm::vec2 texCoord;
|
||||
};
|
||||
|
||||
const std::array VERTICES =
|
||||
{
|
||||
Vertex{.pos = { 0.7f, -0.7f}, .texCoord = {1.f, 1.f}},
|
||||
Vertex{.pos = { 0.7f, 0.7f}, .texCoord = {1.f, 0.f}},
|
||||
Vertex{.pos = {-0.7f, -0.7f}, .texCoord = {0.f, 1.f}},
|
||||
Vertex{.pos = {-0.7f, 0.7f}, .texCoord = {0.f, 0.f}}
|
||||
};
|
||||
}
|
||||
|
||||
void TexturedQuadApp::init(const AppInitArgs& args)
|
||||
{
|
||||
Application::init(args);
|
||||
|
||||
// create shaders
|
||||
sdlpp::GPUShader vertexShader = loadShader("shaders/glsl/textured_triangles_from_buffer.vert.spv", {
|
||||
.format = sdlpp::GPUShaderFormat::SPIRV,
|
||||
.stage = sdlpp::GPUShaderStage::VERTEX
|
||||
});
|
||||
sdlpp::GPUShader fragmentShader = loadShader("shaders/glsl/color_from_texture.frag.spv", {
|
||||
.format = sdlpp::GPUShaderFormat::SPIRV,
|
||||
.stage = sdlpp::GPUShaderStage::FRAGMENT,
|
||||
.numSamplers = 1,
|
||||
.numUniformBuffers = 1
|
||||
});
|
||||
|
||||
// create graphics pipeline
|
||||
std::array colorTargetsDescs = {
|
||||
sdlpp::GPUColorTargetDescription{
|
||||
.format = mDevice.getSwapchainTextureFormat(mWindow),
|
||||
.blendState = {
|
||||
.enableBlend = true,
|
||||
.srcColorBlendfactor = sdlpp::GPUBlendFactor::SRC_ALPHA,
|
||||
.dstColorBlendfactor = sdlpp::GPUBlendFactor::ONE_MINUS_SRC_ALPHA,
|
||||
.colorBlendOp = sdlpp::GPUBlendOp::ADD,
|
||||
.srcAlphaBlendfactor = sdlpp::GPUBlendFactor::ONE,
|
||||
.dstAlphaBlendfactor = sdlpp::GPUBlendFactor::ZERO,
|
||||
.alphaBlendOp = sdlpp::GPUBlendOp::ADD
|
||||
}
|
||||
}
|
||||
};
|
||||
std::array vertexBindings = {
|
||||
sdlpp::GPUVertexBinding{
|
||||
.index = 0,
|
||||
.pitch = sizeof(Vertex)
|
||||
}
|
||||
};
|
||||
std::array vertexAttributes = {
|
||||
sdlpp::GPUVertexAttribute{
|
||||
.location = 0,
|
||||
.bindingIndex = 0,
|
||||
.format = sdlpp::GPUVertexElementFormat::FLOAT2,
|
||||
.offset = offsetof(Vertex, pos)
|
||||
},
|
||||
sdlpp::GPUVertexAttribute{
|
||||
.location = 1,
|
||||
.bindingIndex = 0,
|
||||
.format = sdlpp::GPUVertexElementFormat::FLOAT2,
|
||||
.offset = offsetof(Vertex, texCoord)
|
||||
}
|
||||
};
|
||||
mPipeline.create(mDevice, {
|
||||
.vertexShader = vertexShader,
|
||||
.fragmentShader = fragmentShader,
|
||||
.vertexInputState = {
|
||||
.vertexBindings = vertexBindings,
|
||||
.vertexAttributes = vertexAttributes
|
||||
},
|
||||
.primitiveType = sdlpp::GPUPrimitiveType::TRIANGLESTRIP,
|
||||
.rasterizerState = {
|
||||
.cullMode = sdlpp::GPUCullMode::BACK
|
||||
},
|
||||
.targetInfo = {
|
||||
.colorTargetDescriptions = colorTargetsDescs
|
||||
}
|
||||
});
|
||||
|
||||
// create vertex buffer
|
||||
mVertexBuffer.create(mDevice, {
|
||||
.usage = {.vertex = true},
|
||||
.size = sizeof(VERTICES)
|
||||
});
|
||||
uploadVertexData(mVertexBuffer, std::span(VERTICES.begin(), VERTICES.end()));
|
||||
|
||||
// create texture and sampler
|
||||
sdlpp::GPUTextureCreateArgs textureArgs = {
|
||||
.format = sdlpp::GPUTextureFormat::R8G8B8A8_UNORM_SRGB,
|
||||
.usage = {.sampler = true}
|
||||
};
|
||||
mTexture = loadTexture("bitmaps/sdl.png", textureArgs);
|
||||
mSampler.create(mDevice, {});
|
||||
}
|
||||
|
||||
void TexturedQuadApp::update(const AppUpdateArgs& args)
|
||||
{
|
||||
Application::update(args);
|
||||
|
||||
sdlpp::GPUCommandBuffer cmdBuffer = mDevice.acquireCommandBuffer();
|
||||
Uint32 swapchainWidth = 0, swapchainHeight = 0;
|
||||
sdlpp::GPUTexture swapchainTexture = cmdBuffer.acquireSwapchainTexture(mWindow, swapchainWidth, swapchainHeight);
|
||||
std::array colorTargets = {sdlpp::GPUColorTargetInfo{
|
||||
.texture = swapchainTexture,
|
||||
.clearColor = {.r = 1.f, .g = 0.f, .b = 0.f, .a = 1.f},
|
||||
.loadOp = sdlpp::GPULoadOp::CLEAR,
|
||||
}};
|
||||
sdlpp::GPURenderPass renderPass = cmdBuffer.beginRenderPass({
|
||||
.colorTargetInfos = colorTargets
|
||||
});
|
||||
static const glm::vec4 WHITE(1.f, 1.f, 1.f, 1.f);
|
||||
cmdBuffer.pushFragmentUniformData(0, std::span(&WHITE, 1));
|
||||
renderPass.bindFragmentSampler({.texture = mTexture, .sampler = mSampler});
|
||||
renderPass.bindGraphicsPipeline(mPipeline);
|
||||
renderPass.bindVertexBuffer({.buffer = mVertexBuffer});
|
||||
renderPass.drawPrimitives({.numVertices = VERTICES.size()});
|
||||
renderPass.end();
|
||||
cmdBuffer.submit();
|
||||
}
|
||||
}
|
24
private/sdl_gpu_test/1_textured_quad/app.hpp
Normal file
24
private/sdl_gpu_test/1_textured_quad/app.hpp
Normal file
@ -0,0 +1,24 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_1_TEXTURED_QUAD_APP_HPP_INCLUDED)
|
||||
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_1_TEXTURED_QUAD_APP_HPP_INCLUDED 1
|
||||
|
||||
#include "../application.hpp"
|
||||
|
||||
namespace sdl_gpu_test
|
||||
{
|
||||
class TexturedQuadApp : public Application
|
||||
{
|
||||
private:
|
||||
sdlpp::GPUBuffer mVertexBuffer;
|
||||
sdlpp::GPUGraphicsPipeline mPipeline;
|
||||
sdlpp::GPUTexture mTexture;
|
||||
sdlpp::GPUSampler mSampler;
|
||||
public:
|
||||
void init(const AppInitArgs& args) override;
|
||||
void update(const AppUpdateArgs& args) override;
|
||||
};
|
||||
} // namespace sdl_gpu_test
|
||||
|
||||
#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_1_TEXTURED_QUAD_APP_HPP_INCLUDED)
|
@ -5,18 +5,14 @@ src_files = Split("""
|
||||
main.cpp
|
||||
|
||||
application.cpp
|
||||
util/bitmap.cpp
|
||||
|
||||
0_triangle_with_texcoords/app.cpp
|
||||
1_textured_quad/app.cpp
|
||||
""")
|
||||
|
||||
shader_files = Split("""
|
||||
#assets/shaders/glsl/color_from_uniform.frag
|
||||
#assets/shaders/glsl/color_from_texcoord.frag
|
||||
#assets/shaders/glsl/green.frag
|
||||
#assets/shaders/glsl/triangle.vert
|
||||
#assets/shaders/glsl/triangles_from_buffer.vert
|
||||
#assets/shaders/glsl/textured_triangles_from_buffer.vert
|
||||
""")
|
||||
shader_files = env.Glob("#assets/shaders/glsl/*.frag") \
|
||||
+ env.Glob("#assets/shaders/glsl/*.vert")
|
||||
|
||||
env.Append(CPPDEFINES = ['GLM_FORCE_DEPTH_ZERO_TO_ONE', 'GLM_FORCE_RADIANS'])
|
||||
prog_app = env.UnityProgram(
|
||||
|
@ -3,6 +3,10 @@
|
||||
|
||||
#include <mijin/util/variant.hpp>
|
||||
#include <mijin/virtual_filesystem/relative.hpp>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <mijin/util/winundef.hpp>
|
||||
|
||||
#include "./util/bitmap.hpp"
|
||||
|
||||
namespace sdl_gpu_test
|
||||
{
|
||||
@ -19,6 +23,16 @@ void Application::init(const AppInitArgs& args)
|
||||
});
|
||||
mDevice.claimWindow(mWindow);
|
||||
|
||||
// setup swapchain
|
||||
if (mDevice.windowSupportsSwapchainComposition(mWindow, sdlpp::GPUSwapchainComposition::SDR_LINEAR))
|
||||
{
|
||||
mDevice.setSwapchainParameters(mWindow, sdlpp::GPUSwapchainComposition::SDR_LINEAR, sdlpp::GPUPresentMode::VSYNC);
|
||||
}
|
||||
else
|
||||
{
|
||||
spdlog::warn("Swapchain does not support SRGB, image will not look correct.");
|
||||
}
|
||||
|
||||
fs::path executablePath = fs::absolute(fs::path(args.programArgs[0])).parent_path();
|
||||
mFileSystem.emplaceAdapter<mijin::RelativeFileSystemAdapter<mijin::OSFileSystemAdapter>>(executablePath.parent_path() / "assets");
|
||||
}
|
||||
@ -82,4 +96,45 @@ sdlpp::GPUShader Application::loadShader(const fs::path& path, sdlpp::GPUShaderC
|
||||
shader.create(mDevice, args);
|
||||
return shader;
|
||||
}
|
||||
|
||||
sdlpp::GPUTexture Application::loadTexture(const fs::path& path, sdlpp::GPUTextureCreateArgs& inout_args)
|
||||
{
|
||||
const Bitmap bitmap = loadBitmap(mFileSystem.getPath(path));
|
||||
inout_args.width = bitmap.width,
|
||||
inout_args.height = bitmap.height;
|
||||
|
||||
sdlpp::GPUTexture texture;
|
||||
texture.create(mDevice, inout_args);
|
||||
uploadTextureData(texture, bitmap);
|
||||
return texture;
|
||||
}
|
||||
|
||||
void Application::uploadTextureData(const sdlpp::GPUTexture& texture, const Bitmap& bitmap) const
|
||||
{
|
||||
sdlpp::GPUTransferBuffer transferBuffer;
|
||||
transferBuffer.create(mDevice, {
|
||||
.usage = sdlpp::GPUTransferBufferUsage::UPLOAD,
|
||||
.size = static_cast<Uint32>(bitmap.pixels.size() * sizeof(Pixel))
|
||||
});
|
||||
void* ptr = transferBuffer.map();
|
||||
std::memcpy(ptr, bitmap.pixels.data(), bitmap.pixels.size() * sizeof(Pixel));
|
||||
transferBuffer.unmap();
|
||||
|
||||
sdlpp::GPUCommandBuffer cmdBuffer = mDevice.acquireCommandBuffer();
|
||||
sdlpp::GPUCopyPass copyPass = cmdBuffer.beginCopyPass();
|
||||
copyPass.uploadToGPUTexture(
|
||||
/* source = */ {
|
||||
.transferBuffer = transferBuffer,
|
||||
.pixelsPerRow = bitmap.width,
|
||||
.rowsPerLayer = bitmap.height
|
||||
},
|
||||
/* destination = */ {
|
||||
.texture = texture,
|
||||
.width = bitmap.width,
|
||||
.height = bitmap.height
|
||||
}
|
||||
);
|
||||
copyPass.end();
|
||||
cmdBuffer.submit();
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,11 @@ public:
|
||||
[[nodiscard]]
|
||||
sdlpp::GPUShader loadShader(const fs::path& path, sdlpp::GPUShaderCreateArgs args);
|
||||
|
||||
[[nodiscard]]
|
||||
sdlpp::GPUTexture loadTexture(const fs::path& path, sdlpp::GPUTextureCreateArgs& inout_args);
|
||||
|
||||
void uploadTextureData(const sdlpp::GPUTexture& texture, const struct Bitmap& bitmap) const;
|
||||
|
||||
template<typename TVertex>
|
||||
void uploadVertexData(const sdlpp::GPUBuffer& vertexBuffer, std::span<TVertex> vertices) const;
|
||||
};
|
||||
|
@ -1,5 +1,6 @@
|
||||
|
||||
#include "./0_triangle_with_texcoords/app.hpp"
|
||||
#include "./1_textured_quad/app.hpp"
|
||||
|
||||
#include <mijin/debug/stacktrace.hpp>
|
||||
#include <spdlog/spdlog.h>
|
||||
@ -14,13 +15,15 @@ int main(int argc, char* argv[])
|
||||
|
||||
if (SDL_Init(0) != SDL_TRUE)
|
||||
{
|
||||
throw std::runtime_error("Error initializing SDL.");
|
||||
spdlog::error("Error initializing SDL.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// make sure app is destructed before shutting down SDL
|
||||
std::unique_ptr<sdl_gpu_test::Application> app = std::make_unique<sdl_gpu_test::TriangleWithTexcoordsApp>();
|
||||
// std::unique_ptr<sdl_gpu_test::Application> app = std::make_unique<sdl_gpu_test::TriangleWithTexcoordsApp>();
|
||||
std::unique_ptr<sdl_gpu_test::Application> app = std::make_unique<sdl_gpu_test::TexturedQuadApp>();
|
||||
app->run(std::span(const_cast<const char**>(argv), argc));
|
||||
}
|
||||
catch (std::exception& exception)
|
||||
|
@ -49,6 +49,32 @@ public:
|
||||
operator THandle*() const noexcept { return mHandle; }
|
||||
};
|
||||
|
||||
template<typename THandle, typename TConcrete>
|
||||
class BaseWithDevice : public Base<THandle, TConcrete>
|
||||
{
|
||||
protected:
|
||||
SDL_GPUDevice* mDevice = nullptr;
|
||||
protected:
|
||||
BaseWithDevice() noexcept = default;
|
||||
BaseWithDevice(THandle* handle, SDL_GPUDevice* device) noexcept : Base<THandle, TConcrete>(handle), mDevice(device) {}
|
||||
BaseWithDevice(BaseWithDevice&& other) noexcept : Base<THandle, TConcrete>(std::exchange(other.mHandle, nullptr)),
|
||||
mDevice(std::exchange(other.mDevice, nullptr)){}
|
||||
|
||||
BaseWithDevice& operator=(BaseWithDevice&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
mDevice = std::exchange(other.mDevice, nullptr);
|
||||
Base<THandle, TConcrete>::operator=(std::move(other));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
public:
|
||||
BaseWithDevice(const BaseWithDevice&) = delete;
|
||||
BaseWithDevice& operator=(const BaseWithDevice&) = delete;
|
||||
auto operator<=>(const BaseWithDevice& other) const noexcept = default;
|
||||
};
|
||||
|
||||
class SDLError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
|
@ -8,6 +8,8 @@
|
||||
|
||||
namespace sdlpp
|
||||
{
|
||||
static_assert(sizeof(SDL_bool) == sizeof(bool)); // we assume this in the whole file...
|
||||
|
||||
//
|
||||
// enums
|
||||
//
|
||||
@ -137,6 +139,14 @@ enum class GPUBlendOp
|
||||
MAX = SDL_GPU_BLENDOP_MAX
|
||||
};
|
||||
|
||||
enum class GPUTextureType
|
||||
{
|
||||
TWOD = SDL_GPU_TEXTURETYPE_2D,
|
||||
TWOD_ARRAY = SDL_GPU_TEXTURETYPE_2D_ARRAY,
|
||||
THREED = SDL_GPU_TEXTURETYPE_3D,
|
||||
CUBE = SDL_GPU_TEXTURETYPE_CUBE
|
||||
};
|
||||
|
||||
enum class GPUTextureFormat
|
||||
{
|
||||
INVALID = SDL_GPU_TEXTUREFORMAT_INVALID,
|
||||
@ -198,6 +208,25 @@ enum class GPUTextureFormat
|
||||
D32_FLOAT_S8_UINT = SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT
|
||||
};
|
||||
|
||||
enum class GPUFilter
|
||||
{
|
||||
NEAREST = SDL_GPU_FILTER_NEAREST,
|
||||
LINEAR = SDL_GPU_FILTER_LINEAR
|
||||
};
|
||||
|
||||
enum class GPUSamplerMipmapMode
|
||||
{
|
||||
NEAREST = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST,
|
||||
LINEAR = SDL_GPU_SAMPLERMIPMAPMODE_LINEAR
|
||||
};
|
||||
|
||||
enum class GPUSamplerAddressMode
|
||||
{
|
||||
REPEAT = SDL_GPU_SAMPLERADDRESSMODE_REPEAT,
|
||||
MIRRORED_REPEAT = SDL_GPU_SAMPLERADDRESSMODE_MIRRORED_REPEAT,
|
||||
CLAMP_TO_EDGE = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE
|
||||
};
|
||||
|
||||
enum class GPUShaderFormat
|
||||
{
|
||||
PRIVATE = SDL_GPU_SHADERFORMAT_PRIVATE,
|
||||
@ -233,6 +262,21 @@ enum class GPUTransferBufferUsage
|
||||
DOWNLOAD = SDL_GPU_TRANSFERBUFFERUSAGE_DOWNLOAD
|
||||
};
|
||||
|
||||
enum class GPUSwapchainComposition
|
||||
{
|
||||
SDR = SDL_GPU_SWAPCHAINCOMPOSITION_SDR,
|
||||
SDR_LINEAR = SDL_GPU_SWAPCHAINCOMPOSITION_SDR_LINEAR,
|
||||
HDR_EXTENDED_LINEAR = SDL_GPU_SWAPCHAINCOMPOSITION_HDR_EXTENDED_LINEAR,
|
||||
HDR10_ST2048 = SDL_GPU_SWAPCHAINCOMPOSITION_HDR10_ST2048
|
||||
};
|
||||
|
||||
enum class GPUPresentMode
|
||||
{
|
||||
VSYNC = SDL_GPU_PRESENTMODE_VSYNC,
|
||||
IMMEDIATE = SDL_GPU_PRESENTMODE_IMMEDIATE,
|
||||
MAILBOX = SDL_GPU_PRESENTMODE_MAILBOX
|
||||
};
|
||||
|
||||
//
|
||||
// bitflags
|
||||
//
|
||||
@ -274,6 +318,21 @@ struct GPUBufferUsageFlags : mijin::BitFlags<GPUBufferUsageFlags>
|
||||
}
|
||||
};
|
||||
|
||||
struct GPUTextureUsageFlags : mijin::BitFlags<GPUTextureUsageFlags>
|
||||
{
|
||||
bool sampler : 1 = false;
|
||||
bool colorTarget : 1 = false;
|
||||
bool depthStencilTarget : 1 = false;
|
||||
bool graphicsStorageRead : 1 = false;
|
||||
bool computeStorageRead : 1 = false;
|
||||
bool computeStorageWrite : 1 = false;
|
||||
|
||||
explicit operator SDL_GPUTextureUsageFlags() const noexcept
|
||||
{
|
||||
return std::bit_cast<std::uint8_t>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// structs
|
||||
//
|
||||
@ -432,8 +491,33 @@ struct GPUBufferRegion
|
||||
};
|
||||
static_assert(sizeof(GPUBufferRegion) == sizeof(SDL_GPUBufferRegion));
|
||||
|
||||
struct GPUTextureTransferInfo
|
||||
{
|
||||
SDL_GPUTransferBuffer* transferBuffer;
|
||||
Uint32 offset = 0;
|
||||
Uint32 pixelsPerRow;
|
||||
Uint32 rowsPerLayer;
|
||||
};
|
||||
static_assert(sizeof(GPUTextureTransferInfo) == sizeof(SDL_GPUTextureTransferInfo));
|
||||
|
||||
struct GPUTextureRegion
|
||||
{
|
||||
SDL_GPUTexture* texture;
|
||||
Uint32 mipLevel = 0;
|
||||
Uint32 layer = 0;
|
||||
Uint32 x = 0;
|
||||
Uint32 y = 0;
|
||||
Uint32 z = 0;
|
||||
Uint32 width;
|
||||
Uint32 height = 1;
|
||||
Uint32 depth = 1;
|
||||
};
|
||||
static_assert(sizeof(GPUTextureRegion) == sizeof(SDL_GPUTextureRegion));
|
||||
|
||||
using GPUBufferBinding = SDL_GPUBufferBinding;
|
||||
|
||||
using GPUTextureSamplerBinding = SDL_GPUTextureSamplerBinding;
|
||||
|
||||
//
|
||||
// classes
|
||||
//
|
||||
@ -492,6 +576,21 @@ public:
|
||||
bindVertexBuffers({&binding, 1}, offset);
|
||||
}
|
||||
|
||||
void bindFragmentSamplers(std::span<const GPUTextureSamplerBinding> textureSamplerBindings, Uint32 firstSlot = 0) const noexcept
|
||||
{
|
||||
SDL_BindGPUFragmentSamplers(
|
||||
/* render_pass = */ mHandle,
|
||||
/* first_slot = */ firstSlot,
|
||||
/* texture_sampler_bindings = */ textureSamplerBindings.data(),
|
||||
/* num_bindings = */ static_cast<Uint32>(textureSamplerBindings.size())
|
||||
);
|
||||
}
|
||||
|
||||
void bindFragmentSampler(const GPUTextureSamplerBinding& binding, Uint32 offset = 0) const noexcept
|
||||
{
|
||||
bindFragmentSamplers({&binding, 1}, offset);
|
||||
}
|
||||
|
||||
void drawPrimitives(const GPUDrawPrimitivesArgs& args) const noexcept
|
||||
{
|
||||
SDL_DrawGPUPrimitives(mHandle, args.numVertices, args.numInstances, args.firstVertex, args.firstInstance);
|
||||
@ -529,33 +628,74 @@ public:
|
||||
void uploadToGPUBuffer(const GPUTransferBufferLocation& source, const GPUBufferRegion& destination, bool cycle = false)
|
||||
{
|
||||
SDL_UploadToGPUBuffer(
|
||||
/* copy_pass = */ mHandle,
|
||||
/* source = */ std::bit_cast<const SDL_GPUTransferBufferLocation*>(&source),
|
||||
/* destination = */ std::bit_cast<const SDL_GPUBufferRegion*>(&destination),
|
||||
/* cycle = */ cycle
|
||||
);
|
||||
/* copy_pass = */ mHandle,
|
||||
/* source = */ std::bit_cast<const SDL_GPUTransferBufferLocation*>(&source),
|
||||
/* destination = */ std::bit_cast<const SDL_GPUBufferRegion*>(&destination),
|
||||
/* cycle = */ cycle
|
||||
);
|
||||
}
|
||||
|
||||
void uploadToGPUTexture(const GPUTextureTransferInfo& source, const GPUTextureRegion& destination, bool cycle = false)
|
||||
{
|
||||
SDL_UploadToGPUTexture(
|
||||
/* copy_pass = */ mHandle,
|
||||
/* source = */ std::bit_cast<const SDL_GPUTextureTransferInfo*>(&source),
|
||||
/* destination = */ std::bit_cast<const SDL_GPUTextureRegion*>(&destination),
|
||||
/* cycle = */ cycle
|
||||
);
|
||||
}
|
||||
|
||||
friend class GPUCommandBuffer;
|
||||
};
|
||||
|
||||
class GPUTexture : public Base<SDL_GPUTexture, GPUTexture>
|
||||
struct GPUTextureCreateArgs
|
||||
{
|
||||
GPUTextureType type = GPUTextureType::TWOD;
|
||||
GPUTextureFormat format = GPUTextureFormat::R8G8B8A8_UNORM;
|
||||
GPUTextureUsageFlags usage;
|
||||
Uint32 width = 1;
|
||||
Uint32 height = 1;
|
||||
Uint32 layerCountOrDepth = 1;
|
||||
Uint32 numLevels = 1;
|
||||
GPUSampleCount sampleCount = GPUSampleCount::ONE;
|
||||
};
|
||||
|
||||
class GPUTexture : public BaseWithDevice<SDL_GPUTexture, GPUTexture>
|
||||
{
|
||||
private:
|
||||
SDL_GPUDevice* mDevice = nullptr;
|
||||
public:
|
||||
GPUTexture() noexcept = default;
|
||||
GPUTexture(const GPUTexture&) = delete;
|
||||
GPUTexture(GPUTexture&& other) noexcept : Base(std::move(other)) {}
|
||||
GPUTexture(GPUTexture&& other) noexcept : BaseWithDevice(std::move(other)) {}
|
||||
|
||||
GPUTexture& operator=(const GPUTexture&) = delete;
|
||||
GPUTexture& operator=(GPUTexture&& other) noexcept
|
||||
{
|
||||
Base::operator=(std::move(other));
|
||||
BaseWithDevice::operator=(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
auto operator<=>(const GPUTexture& other) const noexcept = default;
|
||||
|
||||
void create(SDL_GPUDevice* device, const GPUTextureCreateArgs& args)
|
||||
{
|
||||
MIJIN_ASSERT(mHandle == nullptr, "GPUTexture has already been created.");
|
||||
const SDL_GPUTextureCreateInfo createInfo = {
|
||||
.type = static_cast<SDL_GPUTextureType>(args.type),
|
||||
.format = static_cast<SDL_GPUTextureFormat>(args.format),
|
||||
.usage = static_cast<SDL_GPUTextureUsageFlags>(args.usage),
|
||||
.width = args.width,
|
||||
.height = args.height,
|
||||
.layer_count_or_depth = args.layerCountOrDepth,
|
||||
.num_levels = args.numLevels,
|
||||
.sample_count = static_cast<SDL_GPUSampleCount>(args.sampleCount)
|
||||
};
|
||||
mHandle = SDL_CreateGPUTexture(device, &createInfo);
|
||||
if (mHandle == nullptr)
|
||||
{
|
||||
throw SDLError();
|
||||
}
|
||||
mDevice = device;
|
||||
}
|
||||
|
||||
void destroy() noexcept
|
||||
{
|
||||
if (mHandle != nullptr)
|
||||
@ -573,6 +713,75 @@ public:
|
||||
friend class GPUCommandBuffer;
|
||||
};
|
||||
|
||||
struct GPUSamplerCreateArgs
|
||||
{
|
||||
GPUFilter minFilter = GPUFilter::NEAREST;
|
||||
GPUFilter magFilter = GPUFilter::LINEAR;
|
||||
GPUSamplerMipmapMode mipmapMode = GPUSamplerMipmapMode::LINEAR;
|
||||
GPUSamplerAddressMode addressModeU = GPUSamplerAddressMode::REPEAT;
|
||||
GPUSamplerAddressMode addressModeV = GPUSamplerAddressMode::REPEAT;
|
||||
GPUSamplerAddressMode addressModeW = GPUSamplerAddressMode::REPEAT;
|
||||
float mipLodBias = 0.f;
|
||||
float maxAnisotropy = 1.f;
|
||||
bool enableAnisotropy = false;
|
||||
bool enableCompare = false;
|
||||
GPUCompareOp compareOp;
|
||||
float minLod = 0.f;
|
||||
float maxLod = 1.f;
|
||||
};
|
||||
|
||||
class GPUSampler : public BaseWithDevice<SDL_GPUSampler, GPUSampler>
|
||||
{
|
||||
public:
|
||||
GPUSampler() noexcept = default;
|
||||
GPUSampler(const GPUSampler&) = delete;
|
||||
GPUSampler(GPUSampler&& other) noexcept : BaseWithDevice(std::move(other)) {}
|
||||
|
||||
GPUSampler& operator=(const GPUSampler&) = delete;
|
||||
GPUSampler& operator=(GPUSampler&& other) noexcept
|
||||
{
|
||||
BaseWithDevice::operator=(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
auto operator<=>(const GPUSampler& other) const noexcept = default;
|
||||
|
||||
void create(SDL_GPUDevice* device, const GPUSamplerCreateArgs& args)
|
||||
{
|
||||
MIJIN_ASSERT(mHandle == nullptr, "GPUSampler has already been created.");
|
||||
const SDL_GPUSamplerCreateInfo createInfo = {
|
||||
.min_filter = static_cast<SDL_GPUFilter>(args.minFilter),
|
||||
.mag_filter = static_cast<SDL_GPUFilter>(args.magFilter),
|
||||
.mipmap_mode = static_cast<SDL_GPUSamplerMipmapMode>(args.mipmapMode),
|
||||
.address_mode_u = static_cast<SDL_GPUSamplerAddressMode>(args.addressModeU),
|
||||
.address_mode_v = static_cast<SDL_GPUSamplerAddressMode>(args.addressModeV),
|
||||
.address_mode_w = static_cast<SDL_GPUSamplerAddressMode>(args.addressModeW),
|
||||
.mip_lod_bias = args.mipLodBias,
|
||||
.max_anisotropy = args.maxAnisotropy,
|
||||
.enable_anisotropy = args.enableAnisotropy,
|
||||
.enable_compare = args.enableCompare,
|
||||
.compare_op = static_cast<SDL_GPUCompareOp>(args.compareOp),
|
||||
.min_lod = args.minLod,
|
||||
.max_lod = args.maxLod
|
||||
};
|
||||
mHandle = SDL_CreateGPUSampler(device, &createInfo);
|
||||
if (mHandle == nullptr)
|
||||
{
|
||||
throw SDLError();
|
||||
}
|
||||
mDevice = device;
|
||||
}
|
||||
|
||||
void destroy() noexcept
|
||||
{
|
||||
if (mHandle != nullptr)
|
||||
{
|
||||
SDL_ReleaseGPUSampler(mDevice, mHandle);
|
||||
mHandle = nullptr;
|
||||
mDevice = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct GPUBeginRenderPassArgs
|
||||
{
|
||||
std::span<const GPUColorTargetInfo> colorTargetInfos;
|
||||
@ -697,6 +906,28 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
bool windowSupportsSwapchainComposition(SDL_Window* window, GPUSwapchainComposition swapchainComposition) const noexcept
|
||||
{
|
||||
return SDL_WindowSupportsGPUSwapchainComposition(mHandle, window, static_cast<SDL_GPUSwapchainComposition>(swapchainComposition));
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
bool windowSupportsPresentMode(SDL_Window* window, GPUPresentMode presentMode) const noexcept
|
||||
{
|
||||
return SDL_WindowSupportsGPUPresentMode(mHandle, window, static_cast<SDL_GPUPresentMode>(presentMode));
|
||||
}
|
||||
|
||||
void setSwapchainParameters(SDL_Window* window, GPUSwapchainComposition swapchainComposition,
|
||||
GPUPresentMode presentMode) const
|
||||
{
|
||||
if (!SDL_SetGPUSwapchainParameters(mHandle, window, static_cast<SDL_GPUSwapchainComposition>(swapchainComposition),
|
||||
static_cast<SDL_GPUPresentMode>(presentMode)))
|
||||
{
|
||||
throw SDLError();
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
GPUTextureFormat getSwapchainTextureFormat(SDL_Window* window) const
|
||||
{
|
||||
@ -724,20 +955,17 @@ struct GPUGraphicsPipelineCreateArgs
|
||||
GPUGraphicsPipelineTargetInfo targetInfo;
|
||||
};
|
||||
|
||||
class GPUGraphicsPipeline : public Base<SDL_GPUGraphicsPipeline, GPUGraphicsPipeline>
|
||||
class GPUGraphicsPipeline : public BaseWithDevice<SDL_GPUGraphicsPipeline, GPUGraphicsPipeline>
|
||||
{
|
||||
private:
|
||||
SDL_GPUDevice* mDevice = nullptr;
|
||||
public:
|
||||
GPUGraphicsPipeline() noexcept = default;
|
||||
GPUGraphicsPipeline(const GPUGraphicsPipeline&) = delete;
|
||||
GPUGraphicsPipeline(GPUGraphicsPipeline&& other) noexcept : Base(std::move(other)) {}
|
||||
GPUGraphicsPipeline(GPUGraphicsPipeline&& other) noexcept : BaseWithDevice(std::move(other)) {}
|
||||
|
||||
GPUGraphicsPipeline& operator=(const GPUGraphicsPipeline&) = delete;
|
||||
GPUGraphicsPipeline& operator=(GPUGraphicsPipeline&& other) noexcept
|
||||
{
|
||||
Base::operator=(std::move(other));
|
||||
mDevice = other.mDevice;
|
||||
BaseWithDevice::operator=(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
auto operator<=>(const GPUGraphicsPipeline& other) const noexcept = default;
|
||||
@ -813,19 +1041,17 @@ struct GPUShaderCreateArgs
|
||||
Uint32 numUniformBuffers = 0;
|
||||
};
|
||||
|
||||
class GPUShader : public Base<SDL_GPUShader, GPUShader>
|
||||
class GPUShader : public BaseWithDevice<SDL_GPUShader, GPUShader>
|
||||
{
|
||||
private:
|
||||
SDL_GPUDevice* mDevice = nullptr;
|
||||
public:
|
||||
GPUShader() noexcept = default;
|
||||
GPUShader(const GPUShader&) = delete;
|
||||
GPUShader(GPUShader&& other) noexcept : Base(std::move(other)) {}
|
||||
GPUShader(GPUShader&& other) noexcept : BaseWithDevice(std::move(other)) {}
|
||||
|
||||
GPUShader& operator=(const GPUShader&) = delete;
|
||||
GPUShader& operator=(GPUShader&& other) noexcept
|
||||
{
|
||||
Base::operator=(std::move(other));
|
||||
BaseWithDevice::operator=(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
auto operator<=>(const GPUShader& other) const noexcept = default;
|
||||
@ -870,19 +1096,17 @@ struct GPUBufferCreateArgs
|
||||
Uint32 size;
|
||||
};
|
||||
|
||||
class GPUBuffer : public Base<SDL_GPUBuffer, GPUBuffer>
|
||||
class GPUBuffer : public BaseWithDevice<SDL_GPUBuffer, GPUBuffer>
|
||||
{
|
||||
private:
|
||||
SDL_GPUDevice* mDevice = nullptr;
|
||||
public:
|
||||
GPUBuffer() noexcept = default;
|
||||
GPUBuffer(const GPUBuffer&) = delete;
|
||||
GPUBuffer(GPUBuffer&& other) noexcept : Base(std::move(other)) {}
|
||||
GPUBuffer(GPUBuffer&& other) noexcept : BaseWithDevice(std::move(other)) {}
|
||||
|
||||
GPUBuffer& operator=(const GPUBuffer&) = delete;
|
||||
GPUBuffer& operator=(GPUBuffer&& other) noexcept
|
||||
{
|
||||
Base::operator=(std::move(other));
|
||||
BaseWithDevice::operator=(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
auto operator<=>(const GPUBuffer& other) const noexcept = default;
|
||||
@ -920,19 +1144,17 @@ struct GPUTransferBufferCreateArgs
|
||||
Uint32 size;
|
||||
};
|
||||
|
||||
class GPUTransferBuffer : public Base<SDL_GPUTransferBuffer, GPUTransferBuffer>
|
||||
class GPUTransferBuffer : public BaseWithDevice<SDL_GPUTransferBuffer, GPUTransferBuffer>
|
||||
{
|
||||
private:
|
||||
SDL_GPUDevice* mDevice = nullptr;
|
||||
public:
|
||||
GPUTransferBuffer() noexcept = default;
|
||||
GPUTransferBuffer(const GPUTransferBuffer&) = delete;
|
||||
GPUTransferBuffer(GPUTransferBuffer&& other) noexcept : Base(std::move(other)) {}
|
||||
GPUTransferBuffer(GPUTransferBuffer&& other) noexcept : BaseWithDevice(std::move(other)) {}
|
||||
|
||||
GPUTransferBuffer& operator=(const GPUTransferBuffer&) = delete;
|
||||
GPUTransferBuffer& operator=(GPUTransferBuffer&& other) noexcept
|
||||
{
|
||||
Base::operator=(std::move(other));
|
||||
BaseWithDevice::operator=(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
auto operator<=>(const GPUTransferBuffer& other) const noexcept = default;
|
||||
|
@ -13,7 +13,7 @@ struct WindowFlags : mijin::BitFlags<WindowFlags>
|
||||
bool fullscreen : 1; // 0x0000000000000001 /**< window is in fullscreen mode */
|
||||
bool opengl : 1; // 0x0000000000000002 /**< window usable with OpenGL context */
|
||||
bool occluded : 1; // 0x0000000000000004 /**< window is occluded */
|
||||
bool hidden : 1; // 0x0000000000000008) /**< window is neither mapped onto the desktop nor shown in the taskbar/dock/window list; SDL_ShowWindow( is required for it to become visible */
|
||||
bool hidden : 1; // 0x0000000000000008 /**< window is neither mapped onto the desktop nor shown in the taskbar/dock/window list; SDL_ShowWindow( is required for it to become visible */
|
||||
bool borderless : 1; // 0x0000000000000010 /**< no window decoration */
|
||||
bool resizable : 1; // 0x0000000000000020 /**< window can be resized */
|
||||
bool minimized : 1; // 0x0000000000000040 /**< window is minimized */
|
||||
@ -24,7 +24,7 @@ struct WindowFlags : mijin::BitFlags<WindowFlags>
|
||||
bool external : 1; // 0x0000000000000800 /**< window not created by SDL */
|
||||
bool modal : 1; // 0x0000000000001000 /**< window is modal */
|
||||
bool high_pixel_density : 1; // 0x0000000000002000 /**< window uses high pixel density back buffer if possible */
|
||||
bool mouse_capture : 1; // 0x0000000000004000) /**< window has mouse captured (unrelated to MOUSE_GRABBED */
|
||||
bool mouse_capture : 1; // 0x0000000000004000 /**< window has mouse captured (unrelated to MOUSE_GRABBED */
|
||||
bool mouse_relative_mode : 1; // 0x0000000000008000 /**< window has relative mode enabled */
|
||||
bool always_on_top : 1; // 0x0000000000010000 /**< window should always be above others */
|
||||
bool utility : 1; // 0x0000000000020000 /**< window should be treated as a utility window, not showing in the task bar and window list */
|
||||
|
78
private/sdl_gpu_test/util/bitmap.cpp
Normal file
78
private/sdl_gpu_test/util/bitmap.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
|
||||
#include "./bitmap.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <format>
|
||||
|
||||
#define STBI_ASSERT(x) MIJIN_ASSERT(x, #x)
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb_image.h>
|
||||
#undef STB_IMAGE_IMPLEMENTATION
|
||||
#undef STBI_ASSERT
|
||||
|
||||
namespace sdl_gpu_test
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
||||
int stbiReadCallback(void* user, char* data, int size)
|
||||
{
|
||||
mijin::Stream& stream = *static_cast<mijin::Stream*>(user);
|
||||
std::size_t bytesRead = 0;
|
||||
const mijin::StreamError error = stream.readRaw(data, size, {.partial = true}, &bytesRead);
|
||||
if (error != mijin::StreamError::SUCCESS)
|
||||
{
|
||||
// TODO: return what?
|
||||
return 0;
|
||||
}
|
||||
return static_cast<int>(bytesRead);
|
||||
}
|
||||
|
||||
void stbiSkipCallback(void* user, int bytes)
|
||||
{
|
||||
mijin::Stream& stream = *static_cast<mijin::Stream*>(user);
|
||||
(void) stream.seek(bytes, mijin::SeekMode::RELATIVE);
|
||||
}
|
||||
|
||||
int stbiEofCallback(void* user)
|
||||
{
|
||||
mijin::Stream& stream = *static_cast<mijin::Stream*>(user);
|
||||
return stream.isAtEnd();
|
||||
}
|
||||
}
|
||||
|
||||
Bitmap loadBitmap(const mijin::PathReference& path)
|
||||
{
|
||||
std::unique_ptr<mijin::Stream> stream;
|
||||
mijin::throwOnError(path.open(mijin::FileOpenMode::READ, stream));
|
||||
|
||||
const stbi_io_callbacks callbacks = {
|
||||
.read = &stbiReadCallback,
|
||||
.skip = &stbiSkipCallback,
|
||||
.eof = &stbiEofCallback
|
||||
};
|
||||
|
||||
int width, height, components;
|
||||
stbi_uc* data = stbi_load_from_callbacks(
|
||||
/* clbk = */ &callbacks,
|
||||
/* user = */ stream.get(),
|
||||
/* x = */ &width,
|
||||
/* y = */ &height,
|
||||
/* channels_in_file = */ &components,
|
||||
/* desired_channels = */ 4
|
||||
);
|
||||
if (data == nullptr)
|
||||
{
|
||||
throw std::runtime_error(std::format("Could not load bitmap: {}.", stbi_failure_reason()));
|
||||
}
|
||||
|
||||
std::vector<Pixel> pixels;
|
||||
pixels.resize(static_cast<std::size_t>(width) * height);
|
||||
std::memcpy(pixels.data(), data, pixels.size() * sizeof(Pixel));
|
||||
return {
|
||||
.width = static_cast<unsigned>(width),
|
||||
.height = static_cast<unsigned>(height),
|
||||
.pixels = std::move(pixels)
|
||||
};
|
||||
}
|
||||
}
|
32
private/sdl_gpu_test/util/bitmap.hpp
Normal file
32
private/sdl_gpu_test/util/bitmap.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_UTIL_BITMAP_HPP_INCLUDED)
|
||||
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_UTIL_BITMAP_HPP_INCLUDED 1
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include <mijin/virtual_filesystem/filesystem.hpp>
|
||||
|
||||
namespace sdl_gpu_test
|
||||
{
|
||||
struct Pixel
|
||||
{
|
||||
std::uint8_t r;
|
||||
std::uint8_t g;
|
||||
std::uint8_t b;
|
||||
std::uint8_t a;
|
||||
};
|
||||
struct Bitmap
|
||||
{
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
std::vector<Pixel> pixels;
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
Bitmap loadBitmap(const mijin::PathReference& path);
|
||||
} // namespace sdl_gpu_test
|
||||
|
||||
#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_UTIL_BITMAP_HPP_INCLUDED)
|
Loading…
x
Reference in New Issue
Block a user