Moved everything into an application so we can easily have multiple apps and switch between them.

This commit is contained in:
Patrick 2024-09-14 01:00:51 +02:00
parent 65dd335859
commit 4768f0a6a0
8 changed files with 306 additions and 200 deletions

View File

@ -0,0 +1,115 @@
#include "./app.hpp"
#include <glm/vec2.hpp>
#include <glm/vec4.hpp>
namespace sdl_gpu_test
{
struct Vertex
{
glm::vec2 pos;
glm::vec2 texCoord;
};
void TriangleWithTexcoordsApp::init(std::span<const char*> args)
{
Application::init(args);
// create vertex shader
mijin::TypelessBuffer vertexSpv = getFileContentsBinary("shaders/glsl/textured_triangles_from_buffer.vert.spv");
sdlpp::GPUShader vertexShader;
vertexShader.create(mDevice, {
.code = {static_cast<const Uint8*>(vertexSpv.data()), vertexSpv.byteSize()},
.format = sdlpp::GPUShaderFormat::SPIRV,
.stage = sdlpp::GPUShaderStage::VERTEX
});
// create fragment shader
mijin::TypelessBuffer fragmentSpv = getFileContentsBinary("shaders/glsl/color_from_texcoord.frag.spv");
sdlpp::GPUShader fragmentShader;
fragmentShader.create(mDevice, {
.code = {static_cast<const Uint8*>(fragmentSpv.data()), fragmentSpv.byteSize()},
.format = sdlpp::GPUShaderFormat::SPIRV,
.stage = sdlpp::GPUShaderStage::FRAGMENT,
.numUniformBuffers = 1
});
// create graphics pipeline
std::array colorTargetsDescs = {
sdlpp::GPUColorTargetDescription{
.format = mDevice.getSwapchainTextureFormat(mWindow)
}
};
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
},
.targetInfo = {
.colorTargetDescriptions = colorTargetsDescs
}
});
std::array vertices =
{
Vertex{.pos = {-1.f, -1.f}, .texCoord = {0.f, 0.f}},
Vertex{.pos = { 1.f, -1.f}, .texCoord = {1.f, 0.f}},
Vertex{.pos = { 0.f, 1.f}, .texCoord = {0.5f, 1.f}}
};
// create vertex buffer
mVertexBuffer.create(mDevice, {
.usage = {.vertex = true},
.size = sizeof(vertices)
});
uploadVertexData(mVertexBuffer, std::span(vertices.begin(), vertices.end()));
}
void TriangleWithTexcoordsApp::update()
{
Application::update();
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.bindGraphicsPipeline(mPipeline);
renderPass.bindVertexBuffer({.buffer = mVertexBuffer});
renderPass.drawPrimitives({.numVertices = 3});
renderPass.end();
cmdBuffer.submit();
}
}

View File

@ -0,0 +1,22 @@
#pragma once
#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_0_TRIANGLE_WITH_TEXCOORDS_APP_HPP_INCLUDED)
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_0_TRIANGLE_WITH_TEXCOORDS_APP_HPP_INCLUDED 1
#include "../application.hpp"
namespace sdl_gpu_test
{
class TriangleWithTexcoordsApp : public Application
{
private:
sdlpp::GPUBuffer mVertexBuffer;
sdlpp::GPUGraphicsPipeline mPipeline;
public:
void init(std::span<const char*> args) override;
void update() override;
};
} // namespace sdl_gpu_test
#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_0_TRIANGLE_WITH_TEXCOORDS_APP_HPP_INCLUDED)

View File

@ -3,6 +3,10 @@ Import('env')
src_files = Split(""" src_files = Split("""
main.cpp main.cpp
application.cpp
0_triangle_with_texcoords/app.cpp
""") """)
shader_files = Split(""" shader_files = Split("""
@ -26,7 +30,8 @@ prog_app = env.UnityProgram(
'ref': '76ce83801ade3ac922ad5ba6fddc49764c24206a' 'ref': '76ce83801ade3ac922ad5ba6fddc49764c24206a'
} }
}, },
'spdlog': {} 'spdlog': {},
'stb': {}
} }
) )
env.Default(prog_app) env.Default(prog_app)

View File

@ -0,0 +1,74 @@
#include "./application.hpp"
#include <mijin/util/variant.hpp>
#include <mijin/virtual_filesystem/relative.hpp>
namespace sdl_gpu_test
{
void Application::init(std::span<const char*> args)
{
mWindow.create({
.title = "SDL_gpu Test",
.flags = {.resizable = true}
});
mDevice.create({
.formatFlags{.spirv = true},
.debugMode = true
});
mDevice.claimWindow(mWindow);
fs::path executablePath = fs::absolute(fs::path(args[0])).parent_path();
mFileSystem.emplaceAdapter<mijin::RelativeFileSystemAdapter<mijin::OSFileSystemAdapter>>(executablePath.parent_path() / "assets");
}
void Application::cleanup()
{
}
void Application::update()
{
}
void Application::run(std::span<const char*> args)
{
init(args);
while (mRunning)
{
std::optional<sdlpp::sdl_event_t> event;
while ((event = sdlpp::pollEvent()).has_value())
{
std::visit(mijin::Visitor{
[&](const sdlpp::QuitEvent&) { mRunning = false; },
[](const auto&) {} // default handler
}, *event);
}
update();
}
cleanup();
}
[[nodiscard]] [[maybe_unused]]
std::string Application::getFileContentsText(const fs::path& path)
{
std::unique_ptr<mijin::Stream> stream;
mijin::throwOnError(mFileSystem.open(path, mijin::FileOpenMode::READ, stream),
"Error opening file for reading.");
std::string content;
mijin::throwOnError(stream->readAsString(content), "Error reading file contents.");
return content;
}
mijin::TypelessBuffer Application::getFileContentsBinary(const fs::path& path)
{
std::unique_ptr<mijin::Stream> stream;
mijin::throwOnError(mFileSystem.open(path, mijin::FileOpenMode::READ, stream),
"Error opening file for reading.");
mijin::TypelessBuffer content;
mijin::throwOnError(stream->readRest(content), "Error reading file contents.");
return content;
}
}

View File

@ -0,0 +1,72 @@
#pragma once
#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_APPLICATION_HPP_INCLUDED)
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_APPLICATION_HPP_INCLUDED 1
#include <cstring>
#include <mijin/virtual_filesystem/stacked.hpp>
#include "./sdlpp/event.hpp"
#include "./sdlpp/gpu.hpp"
#include "./sdlpp/window.hpp"
namespace sdl_gpu_test
{
class Application
{
protected:
sdlpp::Window mWindow;
sdlpp::GPUDevice mDevice;
mijin::StackedFileSystemAdapter mFileSystem;
bool mRunning = true;
public:
virtual ~Application() noexcept = default;
virtual void init(std::span<const char*> args);
virtual void cleanup();
virtual void update();
void run(std::span<const char*> args);
// utility stuff
[[nodiscard]]
std::string getFileContentsText(const fs::path& path);
[[nodiscard]]
mijin::TypelessBuffer getFileContentsBinary(const fs::path& path);
template<typename TVertex>
void uploadVertexData(const sdlpp::GPUBuffer& vertexBuffer, std::span<TVertex> vertices);
};
template<typename TVertex>
void Application::uploadVertexData(const sdlpp::GPUBuffer& vertexBuffer, std::span<TVertex> vertices)
{
sdlpp::GPUTransferBuffer transferBuffer;
transferBuffer.create(mDevice, {
.usage = sdlpp::GPUTransferBufferUsage::UPLOAD,
.size = static_cast<Uint32>(vertices.size_bytes())
});
void* ptr = transferBuffer.map();
std::memcpy(ptr, vertices.data(), vertices.size_bytes());
transferBuffer.unmap();
sdlpp::GPUCommandBuffer cmdBuffer = mDevice.acquireCommandBuffer();
sdlpp::GPUCopyPass copyPass = cmdBuffer.beginCopyPass();
copyPass.uploadToGPUBuffer(
/* source = */ {
.transferBuffer = transferBuffer
},
/* destination = */ {
.buffer = vertexBuffer,
.size = static_cast<Uint32>(vertices.size_bytes())
}
);
copyPass.end();
cmdBuffer.submit();
}
} // namespace sdl_gpu_test
#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_APPLICATION_HPP_INCLUDED)

View File

@ -1,83 +1,10 @@
#include <glm/vec2.hpp> #include "./0_triangle_with_texcoords/app.hpp"
#include <glm/vec4.hpp>
#include <mijin/util/scope_guard.hpp> #include <mijin/debug/stacktrace.hpp>
#include <mijin/util/variant.hpp>
#include <mijin/virtual_filesystem/filesystem.hpp>
#include <mijin/virtual_filesystem/relative.hpp>
#include <mijin/virtual_filesystem/stacked.hpp>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include <mijin/util/winundef.hpp> #include <mijin/util/winundef.hpp>
#include "./sdlpp/event.hpp"
#include "./sdlpp/gpu.hpp"
#include "./sdlpp/window.hpp"
namespace
{
struct Vertex
{
glm::vec2 pos;
glm::vec2 texCoord;
};
mijin::StackedFileSystemAdapter gFileSystem;
void initFileSystem(const fs::path& executablePath) noexcept
{
gFileSystem.emplaceAdapter<mijin::RelativeFileSystemAdapter<mijin::OSFileSystemAdapter>>(executablePath.parent_path() / "assets");
}
[[nodiscard]] [[maybe_unused]]
std::string getFileContentsText(const fs::path& path)
{
std::unique_ptr<mijin::Stream> stream;
mijin::throwOnError(gFileSystem.open(path, mijin::FileOpenMode::READ, stream),
"Error opening file for reading.");
std::string content;
mijin::throwOnError(stream->readAsString(content), "Error reading file contents.");
return content;
}
[[nodiscard]]
mijin::TypelessBuffer getFileContentsBinary(const fs::path& path)
{
std::unique_ptr<mijin::Stream> stream;
mijin::throwOnError(gFileSystem.open(path, mijin::FileOpenMode::READ, stream),
"Error opening file for reading.");
mijin::TypelessBuffer content;
mijin::throwOnError(stream->readRest(content), "Error reading file contents.");
return content;
}
template<typename TVertex>
void uploadVertexData(const sdlpp::GPUDevice& gpuDevice, const sdlpp::GPUBuffer& vertexBuffer, std::span<TVertex> vertices)
{
sdlpp::GPUTransferBuffer transferBuffer;
transferBuffer.create(gpuDevice, {
.usage = sdlpp::GPUTransferBufferUsage::UPLOAD,
.size = static_cast<Uint32>(vertices.size_bytes())
});
void* ptr = transferBuffer.map();
std::memcpy(ptr, vertices.data(), vertices.size_bytes());
transferBuffer.unmap();
sdlpp::GPUCommandBuffer cmdBuffer = gpuDevice.acquireCommandBuffer();
sdlpp::GPUCopyPass copyPass = cmdBuffer.beginCopyPass();
copyPass.uploadToGPUBuffer(
/* source = */ {
.transferBuffer = transferBuffer
},
/* destination = */ {
.buffer = vertexBuffer,
.size = static_cast<Uint32>(vertices.size_bytes())
}
);
copyPass.end();
cmdBuffer.submit();
}
}
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
if (argc < 1) if (argc < 1)
@ -85,137 +12,27 @@ int main(int argc, char* argv[])
return 1; return 1;
} }
initFileSystem(fs::absolute(fs::path(argv[0])).parent_path());
// init SDL
if (SDL_Init(0) != SDL_TRUE) if (SDL_Init(0) != SDL_TRUE)
{ {
spdlog::error("Error initializing SDL."); throw std::runtime_error("Error initializing SDL.");
return 1;
} }
MIJIN_SCOPE_EXIT {
SDL_Quit();
};
sdlpp::Window window; try
window.create({
.title = "SDL_gpu Test",
.flags = {.resizable = true}
});
sdlpp::GPUDevice gpuDevice;
gpuDevice.create({
.formatFlags{.spirv = true},
.debugMode = true
});
gpuDevice.claimWindow(window);
// create vertex shader
mijin::TypelessBuffer vertexSpv = getFileContentsBinary("shaders/glsl/textured_triangles_from_buffer.vert.spv");
sdlpp::GPUShader vertexShader;
vertexShader.create(gpuDevice, {
.code = {static_cast<const Uint8*>(vertexSpv.data()), vertexSpv.byteSize()},
.format = sdlpp::GPUShaderFormat::SPIRV,
.stage = sdlpp::GPUShaderStage::VERTEX
});
// create fragment shader
mijin::TypelessBuffer fragmentSpv = getFileContentsBinary("shaders/glsl/color_from_texcoord.frag.spv");
sdlpp::GPUShader fragmentShader;
fragmentShader.create(gpuDevice, {
.code = {static_cast<const Uint8*>(fragmentSpv.data()), fragmentSpv.byteSize()},
.format = sdlpp::GPUShaderFormat::SPIRV,
.stage = sdlpp::GPUShaderStage::FRAGMENT,
.numUniformBuffers = 1
});
// create graphics pipeline
std::array colorTargetsDescs = {
sdlpp::GPUColorTargetDescription{
.format = gpuDevice.getSwapchainTextureFormat(window)
}
};
sdlpp::GPUGraphicsPipeline pipeline;
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)
}
};
pipeline.create(gpuDevice, {
.vertexShader = vertexShader,
.fragmentShader = fragmentShader,
.vertexInputState = {
.vertexBindings = vertexBindings,
.vertexAttributes = vertexAttributes
},
.targetInfo = {
.colorTargetDescriptions = colorTargetsDescs
}
});
std::array vertices =
{ {
Vertex{.pos = {-1.f, -1.f}, .texCoord = {0.f, 0.f}}, // make sure app is destructed before shutting down SDL
Vertex{.pos = { 1.f, -1.f}, .texCoord = {1.f, 0.f}}, std::unique_ptr<sdl_gpu_test::Application> app = std::make_unique<sdl_gpu_test::TriangleWithTexcoordsApp>();
Vertex{.pos = { 0.f, 1.f}, .texCoord = {0.5f, 1.f}} app->run(std::span(const_cast<const char**>(argv), argc));
}; }
catch (std::exception& exception)
// create vertex buffer
sdlpp::GPUBuffer vertexBuffer;
vertexBuffer.create(gpuDevice, {
.usage = {.vertex = true},
.size = sizeof(vertices)
});
uploadVertexData(gpuDevice, vertexBuffer, std::span(vertices.begin(), vertices.end()));
bool running = true;
while(running)
{ {
std::optional<sdlpp::sdl_event_t> event; spdlog::error("Exception while running application: {}", exception.what());
while ((event = sdlpp::pollEvent()).has_value()) mijin::getExceptionStacktrace().then([](const mijin::Stacktrace& stacktrace)
{ {
std::visit(mijin::Visitor{ spdlog::error("{}", stacktrace);
[&](const sdlpp::QuitEvent&) { running = false; },
[](const auto&) {} // default handler
}, *event);
}
sdlpp::GPUCommandBuffer cmdBuffer = gpuDevice.acquireCommandBuffer();
Uint32 swapchainWidth = 0, swapchainHeight = 0;
sdlpp::GPUTexture swapchainTexture = cmdBuffer.acquireSwapchainTexture(window, 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.bindGraphicsPipeline(pipeline);
renderPass.bindVertexBuffer({.buffer = vertexBuffer});
renderPass.drawPrimitives({.numVertices = 3});
renderPass.end();
cmdBuffer.submit();
} }
SDL_Quit();
return 0; return 0;
} }

View File

@ -4,6 +4,7 @@
#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SDLPP_COMMON_HPP_INCLUDED) #if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SDLPP_COMMON_HPP_INCLUDED)
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SDLPP_COMMON_HPP_INCLUDED 1 #define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SDLPP_COMMON_HPP_INCLUDED 1
#include <cstdint>
#include <optional> #include <optional>
#include <span> #include <span>
#include <stdexcept> #include <stdexcept>

View File

@ -33,7 +33,7 @@ using sdl_event_t = std::variant<
>; >;
[[nodiscard]] [[nodiscard]]
std::optional<sdl_event_t> pollEvent() noexcept inline std::optional<sdl_event_t> pollEvent() noexcept
{ {
SDL_Event event; SDL_Event event;
while (SDL_PollEvent(&event)) while (SDL_PollEvent(&event))