Added simple post-processing.

This commit is contained in:
Patrick 2024-10-01 16:56:28 +02:00
parent 559ee5f3c3
commit 16135d8fcd
16 changed files with 670 additions and 35 deletions

View File

@ -0,0 +1,19 @@
#version 460
layout(local_size_x = 16, local_size_y = 16, local_size_x = 1) in;
layout(set = 0, binding = 0, rgba8) uniform readonly image2D u_sourceImage;
layout(set = 1, binding = 0, rgba8) uniform writeonly image2D u_targetImage;
float luma(vec4 color)
{
return dot(color.rgb, vec3(0.299, 0.587, 0.114));
}
void main()
{
const ivec2 uv = ivec2(gl_GlobalInvocationID.xy);
const vec4 pixel = imageLoad(u_sourceImage, uv);
const float lum = luma(pixel);
imageStore(u_targetImage, uv, vec4(lum, lum, lum, 1.0));
}

View File

@ -121,11 +121,7 @@ void InputApp::init(const AppInitArgs& args)
mSampler.create(mDevice, {}); mSampler.create(mDevice, {});
// open gamepad // open gamepad
const std::vector<SDL_JoystickID> gamepads = sdlpp::getGamepads(); mGamepad = openFirstGamepad();
if (!gamepads.empty())
{
mGamepad.open(gamepads[0]);
}
} }
void InputApp::update(const AppUpdateArgs& args) void InputApp::update(const AppUpdateArgs& args)

View File

@ -64,11 +64,7 @@ void UIApp::init(const AppInitArgs& args)
mSampler.create(mDevice, {}); mSampler.create(mDevice, {});
// open gamepad // open gamepad
const std::vector<SDL_JoystickID> gamepads = sdlpp::getGamepads(); mGamepad = openFirstGamepad();
if (!gamepads.empty())
{
mGamepad.open(gamepads[0]);
}
// init the UI // init the UI
mWidgetTree.init({.renderer = &mUIRenderer}); mWidgetTree.init({.renderer = &mUIRenderer});

View File

@ -43,10 +43,7 @@ void ThreeDSceneApp::init(const AppInitArgs& args)
// open gamepad // open gamepad
const std::vector<SDL_JoystickID> gamepads = sdlpp::getGamepads(); const std::vector<SDL_JoystickID> gamepads = sdlpp::getGamepads();
if (!gamepads.empty()) mGamepad = openFirstGamepad();
{
mGamepad.open(gamepads[0]);
}
// init the UI // init the UI
mWidgetTree.init({.renderer = &mUIRenderer}); mWidgetTree.init({.renderer = &mUIRenderer});
@ -57,21 +54,10 @@ void ThreeDSceneApp::init(const AppInitArgs& args)
.posY = 100, .posY = 100,
.textHeight = 20 .textHeight = 20
}); });
mButton = mWidgetTree.getRootWidget().emplaceChild<Button>({
.text = "Click Me!",
.posX = 100,
.posY = 500
});
// init the scene // init the scene
mScene.init({.renderer = &mSceneRenderer}); mScene.init({.renderer = &mSceneRenderer});
loadScene("scenes/test_scene.yml", mScene); loadScene("scenes/test_scene.yml", mScene);
// mScene.getRootNode().emplaceChild<MeshNode>({.mesh = "meshes/cube.obj", .texture = "meshes/cube.png"});
mButton->clicked.connect([&]()
{
mButton->setText("Thanks!");
});
mWindow.setRelativeMouseMode(true); mWindow.setRelativeMouseMode(true);
} }

View File

@ -5,7 +5,6 @@
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_7_3D_SCENE_APP_HPP_INCLUDED 1 #define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_7_3D_SCENE_APP_HPP_INCLUDED 1
#include "../application.hpp" #include "../application.hpp"
#include "../gui/button.hpp"
#include "../gui/label.hpp" #include "../gui/label.hpp"
#include "../gui/widget.hpp" #include "../gui/widget.hpp"
#include "../gui/ui_renderer.hpp" #include "../gui/ui_renderer.hpp"
@ -27,7 +26,6 @@ private:
Scene mScene; Scene mScene;
Label* mLabel = nullptr; Label* mLabel = nullptr;
Button* mButton = nullptr;
sdlpp::Gamepad mGamepad; sdlpp::Gamepad mGamepad;
Uint32 mLastSwapchainWidth = 1280; Uint32 mLastSwapchainWidth = 1280;

View File

@ -0,0 +1,263 @@
#include "./app.hpp"
#include <numbers>
#include <glm/mat4x4.hpp>
#include <glm/vec2.hpp>
#include <glm/vec4.hpp>
#include <glm/gtx/norm.hpp>
#include "../scene/mesh.hpp"
#include "../scene/loader.hpp"
#include "../sdlpp/keyboard.hpp"
#include "../util/bitmap.hpp"
#include "../util/mesh.hpp"
#include "../util/spdlog_wrapper.hpp"
namespace sdl_gpu_test
{
namespace
{
inline constexpr float PITCH_MIN = -0.49f * std::numbers::pi_v<float>;
inline constexpr float PITCH_MAX = 0.49f * std::numbers::pi_v<float>;
inline constexpr Uint16 AXIS_DEADZONE = 5000;
inline constexpr sdlpp::GPUTextureFormat COLOR_BUFFER_FORMAT = sdlpp::GPUTextureFormat::R32G32B32A32_FLOAT;
inline constexpr sdlpp::GPUTextureFormat POSTPROCESS_BUFFER_FORMAT = sdlpp::GPUTextureFormat::R8G8B8A8_UNORM;
}
void PostProcessApp::init(const AppInitArgs& args)
{
Application::init(args);
// create color buffer
mColorBuffer.create(mDevice, {
.format = sdlpp::GPUTextureFormat::R32G32B32A32_FLOAT,
.usage = sdlpp::GPUTextureUsageFlags{.colorTarget = true, .computeStorageRead = true},
.width = 1280,
.height = 720
});
// create depth buffer
mDepthBuffer.create(mDevice, {
.format = sdlpp::GPUTextureFormat::D16_UNORM,
.usage = sdlpp::GPUTextureUsageFlags{.depthStencilTarget = true},
.width = 1280,
.height = 720
});
// and the target for the compute pass (SDL_gpu doesn't support using the swapchain in compute shaders)
mPostProcessBuffer.create(mDevice, {
.format = POSTPROCESS_BUFFER_FORMAT,
.usage = sdlpp::GPUTextureUsageFlags{.computeStorageWrite = true},
.width = 1280,
.height = 720
});
// init UI renderer
mUIRenderer.init(*this);
// init scene renderer
mSceneRenderer.init(*this, {
.colorBufferFormat = COLOR_BUFFER_FORMAT
});
// and the post processor
mPostProcessor.init(*this);
// open gamepad
mGamepad = openFirstGamepad();
// init the UI
mWidgetTree.init({.renderer = &mUIRenderer});
mLabel = mWidgetTree.getRootWidget().emplaceChild<Label>({
.text = "Test-Text!\nSecond $f00line$fff?",
.color = glm::vec4(0.f, 1.f, 0.f, 1.f),
.posX = 100,
.posY = 100,
.textHeight = 20
});
// init the scene
mScene.init({.renderer = &mSceneRenderer});
loadScene("scenes/test_scene.yml", mScene);
mWindow.setRelativeMouseMode(true);
}
void PostProcessApp::update(const AppUpdateArgs& args)
{
Application::update(args);
processInput(args);
mLabel->setText(std::format("Pitch: {:.1f}\nYaw: {:.1f}", mPitch, mYaw));
mWidgetTree.revalidateWidgets();
// begin rendering
sdlpp::GPUCommandBuffer cmdBuffer = mDevice.acquireCommandBuffer();
Uint32 swapchainWidth = 0, swapchainHeight = 0;
const sdlpp::GPUTexture swapchainTexture = cmdBuffer.acquireSwapchainTexture(mWindow, swapchainWidth, swapchainHeight);
if (swapchainWidth != mLastSwapchainWidth || swapchainHeight != mLastSwapchainHeight)
{
mColorBuffer.destroy();
mColorBuffer.create(mDevice, {
.format = COLOR_BUFFER_FORMAT,
.usage = sdlpp::GPUTextureUsageFlags{.colorTarget = true, .computeStorageRead = true},
.width = swapchainWidth,
.height = swapchainHeight
});
mDepthBuffer.destroy();
mDepthBuffer.create(mDevice, {
.format = sdlpp::GPUTextureFormat::D16_UNORM,
.usage = sdlpp::GPUTextureUsageFlags{.depthStencilTarget = true},
.width = swapchainWidth,
.height = swapchainHeight
});
mPostProcessBuffer.destroy();
mPostProcessBuffer.create(mDevice, {
.format = POSTPROCESS_BUFFER_FORMAT,
.usage = sdlpp::GPUTextureUsageFlags{.computeStorageWrite = true},
.width = swapchainWidth,
.height = swapchainHeight
});
mLastSwapchainWidth = swapchainWidth;
mLastSwapchainHeight = swapchainHeight;
}
// render the scene
mSceneRenderer.render({
.camera = {
.position = mPosition,
.pitch = mPitch,
.yaw = mYaw
},
.cmdBuffer = &cmdBuffer,
.targetTexture = &mColorBuffer,
.depthTexture = &mDepthBuffer,
.targetTextureWidth = swapchainWidth,
.targetTextureHeight = swapchainHeight
});
// do the post processing
mPostProcessor.execute({
.cmdBuffer = &cmdBuffer,
.sourceTexture = &mColorBuffer,
.targetTexture = &mPostProcessBuffer,
.textureWidth = swapchainWidth,
.textureHeight = swapchainHeight
});
// copy the result to the swapchain image
sdlpp::GPUCopyPass copyPass = cmdBuffer.beginCopyPass();
copyPass.copyTextureToTexture(
/* source = */ {.texture = mPostProcessBuffer},
/* destination = */ {.texture = swapchainTexture},
/* width = */ swapchainWidth,
/* height = */ swapchainHeight
);
copyPass.end();
// render the "UI"
mUIRenderer.render({
.cmdBuffer = &cmdBuffer,
.targetTexture = &swapchainTexture,
.targetTextureWidth = swapchainWidth,
.targetTextureHeight = swapchainHeight
});
// finalize
cmdBuffer.submit();
}
void PostProcessApp::handleKeyboardEvent(const sdlpp::KeyboardEvent& event)
{
switch (event.key)
{
case SDLK_Q:
if (!event.down)
{
mRunning = false;
}
break;
default: break;
}
}
void PostProcessApp::handleMouseMotionEvent(const sdlpp::MouseMotionEvent& event)
{
mWidgetTree.notifyMouseMoved(event);
mYaw -= 0.002f * event.xrel;
mPitch -= 0.002f * event.yrel;
}
void PostProcessApp::handleMouseButtonEvent(const sdlpp::MouseButtonEvent& event)
{
mWidgetTree.notifyMouseButton(event);
}
void PostProcessApp::processInput(const AppUpdateArgs& args)
{
glm::vec2 movement = {0.f, 0.f};
const std::span<const SDL_bool> keyboardState = sdlpp::getKeyboardState();
if (mGamepad)
{
Sint16 axisValue = mGamepad.getAxis(sdlpp::GamepadAxis::RIGHTX);
if (std::abs(axisValue) > AXIS_DEADZONE)
{
mYaw -= 0.0001f * args.tickSeconds * static_cast<float>(axisValue);
}
axisValue = mGamepad.getAxis(sdlpp::GamepadAxis::RIGHTY);
if (std::abs(axisValue) > AXIS_DEADZONE)
{
mPitch -= 0.0001f * args.tickSeconds * static_cast<float>(axisValue);
}
axisValue = mGamepad.getAxis(sdlpp::GamepadAxis::LEFTY);
if (std::abs(axisValue) > AXIS_DEADZONE)
{
movement.y -= static_cast<float>(axisValue) / static_cast<float>(std::numeric_limits<Sint16>::max());
}
axisValue = mGamepad.getAxis(sdlpp::GamepadAxis::LEFTX);
if (std::abs(axisValue) > AXIS_DEADZONE)
{
movement.x += static_cast<float>(axisValue) / static_cast<float>(std::numeric_limits<Sint16>::max());
}
}
if (keyboardState[SDL_SCANCODE_W])
{
movement.y += 1.f;
}
if (keyboardState[SDL_SCANCODE_S])
{
movement.y -= 1.f;
}
if (keyboardState[SDL_SCANCODE_A])
{
movement.x -= 1.f;
}
if (keyboardState[SDL_SCANCODE_D])
{
movement.x += 1.f;
}
if (glm::length2(movement) > 1.f)
{
movement = glm::normalize(movement);
}
mPosition.x -= 2.f * args.tickSeconds * std::sin(mYaw) * movement.y;
mPosition.z -= 2.f * args.tickSeconds * std::cos(mYaw) * movement.y;
mPosition.x += 2.f * args.tickSeconds * std::cos(mYaw) * movement.x;
mPosition.z -= 2.f * args.tickSeconds * std::sin(mYaw) * movement.x;
while (mYaw >= 2.f * std::numbers::pi) { mYaw -= 2.f * std::numbers::pi_v<float>; }
while (mYaw < 0.f) { mYaw += 2.f * std::numbers::pi_v<float>; }
mPitch = std::clamp(mPitch, PITCH_MIN, PITCH_MAX);
}
}

View File

@ -0,0 +1,52 @@
#pragma once
#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_8_POST_PROCESS_APP_HPP_INCLUDED)
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_8_POST_PROCESS_APP_HPP_INCLUDED 1
#include "../application.hpp"
#include "../gui/label.hpp"
#include "../gui/widget.hpp"
#include "../gui/ui_renderer.hpp"
#include "../post_process/post_processor.hpp"
#include "../scene/scene.hpp"
#include "../scene/scene_renderer.hpp"
#include "../sdlpp/gamepad.hpp"
namespace sdl_gpu_test
{
class PostProcessApp : public Application
{
private:
sdlpp::GPUTexture mColorBuffer;
sdlpp::GPUTexture mDepthBuffer;
sdlpp::GPUTexture mPostProcessBuffer;
UIRenderer mUIRenderer;
WidgetTree mWidgetTree;
SceneRenderer mSceneRenderer;
Scene mScene;
PostProcessor mPostProcessor;
Label* mLabel = nullptr;
sdlpp::Gamepad mGamepad;
Uint32 mLastSwapchainWidth = 1280;
Uint32 mLastSwapchainHeight = 720;
glm::vec3 mPosition = {5.f, 0.f, -7.f};
float mYaw = 1.57f;
float mPitch = 0.f;
public:
void init(const AppInitArgs& args) override;
void update(const AppUpdateArgs& args) override;
void handleKeyboardEvent(const sdlpp::KeyboardEvent& event) override;
void handleMouseMotionEvent(const sdlpp::MouseMotionEvent& event) override;
void handleMouseButtonEvent(const sdlpp::MouseButtonEvent&) override;
private:
void processInput(const AppUpdateArgs& args);
};
} // namespace sdl_gpu_test
#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_8_POST_PROCESS_APP_HPP_INCLUDED)

View File

@ -9,6 +9,7 @@ src_files = Split("""
gui/label.cpp gui/label.cpp
gui/ui_renderer.cpp gui/ui_renderer.cpp
gui/widget.cpp gui/widget.cpp
post_process/post_processor.cpp
scene/loader.cpp scene/loader.cpp
scene/mesh.cpp scene/mesh.cpp
scene/scene.cpp scene/scene.cpp
@ -26,10 +27,12 @@ src_files = Split("""
5_input/app.cpp 5_input/app.cpp
6_ui/app.cpp 6_ui/app.cpp
7_3d_scene/app.cpp 7_3d_scene/app.cpp
8_post_process/app.cpp
""") """)
shader_files = env.Glob("#assets/shaders/glsl/*.frag") \ shader_files = env.Glob("#assets/shaders/glsl/*.frag") \
+ env.Glob("#assets/shaders/glsl/*.vert") + env.Glob("#assets/shaders/glsl/*.vert") \
+ env.Glob("#assets/shaders/glsl/*.comp")
env.Append(CPPDEFINES = ['GLM_FORCE_DEPTH_ZERO_TO_ONE', 'GLM_FORCE_RADIANS', 'GLM_ENABLE_EXPERIMENTAL']) env.Append(CPPDEFINES = ['GLM_FORCE_DEPTH_ZERO_TO_ONE', 'GLM_FORCE_RADIANS', 'GLM_ENABLE_EXPERIMENTAL'])
prog_app = env.UnityProgram( prog_app = env.UnityProgram(

View File

@ -114,6 +114,16 @@ sdlpp::GPUShader Application::loadShader(const fs::path& path, sdlpp::GPUShaderC
return shader; return shader;
} }
sdlpp::GPUComputePipeline Application::loadComputePipeline(const fs::path& path, sdlpp::GPUComputePipelineCreateArgs args)
{
mijin::TypelessBuffer source = getFileContentsBinary(path);
sdlpp::GPUComputePipeline pipeline;
args.code = {static_cast<const Uint8*>(source.data()), source.byteSize()};
pipeline.create(mDevice, args);
return pipeline;
}
sdlpp::GPUTexture Application::loadTexture(const fs::path& path, sdlpp::GPUTextureCreateArgs& inout_args) sdlpp::GPUTexture Application::loadTexture(const fs::path& path, sdlpp::GPUTextureCreateArgs& inout_args)
{ {
const Bitmap bitmap = loadBitmap(mFileSystem.getPath(path)); const Bitmap bitmap = loadBitmap(mFileSystem.getPath(path));
@ -159,4 +169,26 @@ void Application::uploadTextureData(const sdlpp::GPUTexture& texture, const Bitm
copyPass.end(); copyPass.end();
cmdBuffer.submit(); cmdBuffer.submit();
} }
sdlpp::Gamepad Application::openFirstGamepad() const
{
sdlpp::Gamepad gamepad;
const std::vector<SDL_JoystickID> gamepads = sdlpp::getGamepads();
if (!gamepads.empty())
{
for (SDL_JoystickID gamepadId : gamepads)
{
try
{
gamepad.open(gamepadId);
break;
}
catch(std::runtime_error& err)
{
spdlog::warn("Error opening gamepad: {}.", err.what());
}
}
}
return gamepad;
}
} }

View File

@ -10,6 +10,7 @@
#include <mijin/virtual_filesystem/stacked.hpp> #include <mijin/virtual_filesystem/stacked.hpp>
#include "./sdlpp/event.hpp" #include "./sdlpp/event.hpp"
#include "./sdlpp/gamepad.hpp"
#include "./sdlpp/gpu.hpp" #include "./sdlpp/gpu.hpp"
#include "./sdlpp/window.hpp" #include "./sdlpp/window.hpp"
@ -74,6 +75,9 @@ public:
[[nodiscard]] [[nodiscard]]
sdlpp::GPUShader loadShader(const fs::path& path, sdlpp::GPUShaderCreateArgs args); sdlpp::GPUShader loadShader(const fs::path& path, sdlpp::GPUShaderCreateArgs args);
[[nodiscard]]
sdlpp::GPUComputePipeline loadComputePipeline(const fs::path& path, sdlpp::GPUComputePipelineCreateArgs args);
[[nodiscard]] [[nodiscard]]
sdlpp::GPUTexture loadTexture(const fs::path& path, sdlpp::GPUTextureCreateArgs& inout_args); sdlpp::GPUTexture loadTexture(const fs::path& path, sdlpp::GPUTextureCreateArgs& inout_args);
@ -81,6 +85,9 @@ public:
void uploadTextureData(const sdlpp::GPUTexture& texture, const struct Bitmap& bitmap) const; void uploadTextureData(const sdlpp::GPUTexture& texture, const struct Bitmap& bitmap) const;
[[nodiscard]]
sdlpp::Gamepad openFirstGamepad() const;
template<typename TVertex> template<typename TVertex>
void uploadVertexData(const sdlpp::GPUBuffer& vertexBuffer, std::span<TVertex> vertices) const; void uploadVertexData(const sdlpp::GPUBuffer& vertexBuffer, std::span<TVertex> vertices) const;
}; };

View File

@ -10,7 +10,9 @@
#include "./5_input/app.hpp" #include "./5_input/app.hpp"
#include "./6_ui/app.hpp" #include "./6_ui/app.hpp"
#include "./7_3d_scene/app.hpp" #include "./7_3d_scene/app.hpp"
#include "./8_post_process/app.hpp"
#include "./util/spdlog_wrapper.hpp" #include "./util/spdlog_wrapper.hpp"
#include "8_post_process/app.hpp"
namespace namespace
{ {
@ -40,7 +42,8 @@ const std::array APPS = {
makeAppHelper<sdl_gpu_test::TexturedCubeApp>("textured_cube"), makeAppHelper<sdl_gpu_test::TexturedCubeApp>("textured_cube"),
makeAppHelper<sdl_gpu_test::InputApp>("input"), makeAppHelper<sdl_gpu_test::InputApp>("input"),
makeAppHelper<sdl_gpu_test::UIApp>("ui"), makeAppHelper<sdl_gpu_test::UIApp>("ui"),
makeAppHelper<sdl_gpu_test::ThreeDSceneApp>("3d_scene") makeAppHelper<sdl_gpu_test::ThreeDSceneApp>("3d_scene"),
makeAppHelper<sdl_gpu_test::PostProcessApp>("post_process")
}; };
} }

View File

@ -0,0 +1,47 @@
#include "./post_processor.hpp"
namespace sdl_gpu_test
{
namespace
{
inline constexpr Uint32 LOCAL_SIZE_X = 16;
inline constexpr Uint32 LOCAL_SIZE_Y = 16;
}
void PostProcessor::init(Application& application)
{
mApplication = &application;
createPipeline();
}
void PostProcessor::createPipeline()
{
mPipeline = mApplication->loadComputePipeline("shaders/glsl/post_process.comp.spv", {
.format = sdlpp::GPUShaderFormat::SPIRV,
.numReadonlyStorageTextures = 1,
.numWriteonlyStorageTextures = 1,
.threadcountX = LOCAL_SIZE_X,
.threadcountY = LOCAL_SIZE_Y
});
}
void PostProcessor::execute(const PostProcessorExecuteArgs& args)
{
const std::array textueBindings = {
sdlpp::GPUStorageTextureWriteOnlyBinding{
.texture = *args.targetTexture,
.cycle = true
}
};
sdlpp::GPUComputePass computePass = args.cmdBuffer->beginComputePass({.storageTextureBindings = textueBindings});
computePass.bindComputePipeline(mPipeline);
computePass.bindStorageTexture(0, *args.sourceTexture);
computePass.dispatch(
/* groupCountX = */ (args.textureWidth + LOCAL_SIZE_X - 1) / LOCAL_SIZE_X,
/* groupCountY = */ (args.textureHeight + LOCAL_SIZE_Y - 1) / LOCAL_SIZE_Y
);
computePass.end();
}
}

View File

@ -0,0 +1,35 @@
#pragma once
#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_POST_PROCESS_POST_PROCESSOR_HPP_INCLUDED)
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_POST_PROCESS_POST_PROCESSOR_HPP_INCLUDED 1
#include "../application.hpp"
#include "../sdlpp/gpu.hpp"
namespace sdl_gpu_test
{
struct PostProcessorExecuteArgs
{
const sdlpp::GPUCommandBuffer* cmdBuffer;
const sdlpp::GPUTexture* sourceTexture;
const sdlpp::GPUTexture* targetTexture;
unsigned textureWidth;
unsigned textureHeight;
};
class PostProcessor
{
private:
Application* mApplication = nullptr;
sdlpp::GPUComputePipeline mPipeline;
public:
void init(Application& application);
void execute(const PostProcessorExecuteArgs& args);
private:
void createPipeline();
};
} // namespace sdl_gpu_test
#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_POST_PROCESS_POST_PROCESSOR_HPP_INCLUDED)

View File

@ -24,11 +24,17 @@ struct PerModelParameters
glm::mat4 modelToWorld; glm::mat4 modelToWorld;
}; };
} }
void SceneRenderer::init(Application& application) void SceneRenderer::init(Application& application, SceneRendererInitArgs args)
{ {
mApplication = &application; mApplication = &application;
createPipeline(); if (args.colorBufferFormat == sdlpp::GPUTextureFormat::INVALID)
{
// default to render directly to the swapchain
args.colorBufferFormat = mApplication->getDevice().getSwapchainTextureFormat(mApplication->getWindow());
}
createPipeline(args);
mSampler.create(mApplication->getDevice(), {}); mSampler.create(mApplication->getDevice(), {});
} }
@ -100,6 +106,7 @@ void SceneRenderer::render(const SceneRendererRenderArgs& args)
.texture = *args.targetTexture, .texture = *args.targetTexture,
.clearColor = {.r = 0.f, .g = 0.f, .b = 0.f, .a = 1.f}, .clearColor = {.r = 0.f, .g = 0.f, .b = 0.f, .a = 1.f},
.loadOp = sdlpp::GPULoadOp::CLEAR, .loadOp = sdlpp::GPULoadOp::CLEAR,
.cycle = true
}}; }};
sdlpp::GPURenderPass renderPass = args.cmdBuffer->beginRenderPass({ sdlpp::GPURenderPass renderPass = args.cmdBuffer->beginRenderPass({
.colorTargetInfos = colorTargets, .colorTargetInfos = colorTargets,
@ -193,7 +200,7 @@ bool SceneRenderer::updateRenderListEntry(mesh_id_t meshId, const RenderListUpda
return true; return true;
} }
void SceneRenderer::createPipeline() void SceneRenderer::createPipeline(const SceneRendererInitArgs& args)
{ {
// create shaders // create shaders
const sdlpp::GPUShader vertexShader = mApplication->loadShader("shaders/glsl/scene_renderer.vert.spv", { const sdlpp::GPUShader vertexShader = mApplication->loadShader("shaders/glsl/scene_renderer.vert.spv", {
@ -209,7 +216,7 @@ void SceneRenderer::createPipeline()
}); });
std::array colorTargetsDescs = { std::array colorTargetsDescs = {
sdlpp::GPUColorTargetDescription{ sdlpp::GPUColorTargetDescription{
.format = mApplication->getDevice().getSwapchainTextureFormat(mApplication->getWindow()), .format = args.colorBufferFormat,
.blendState = { .blendState = {
.enableBlend = true, .enableBlend = true,
.srcColorBlendfactor = sdlpp::GPUBlendFactor::SRC_ALPHA, .srcColorBlendfactor = sdlpp::GPUBlendFactor::SRC_ALPHA,

View File

@ -37,6 +37,11 @@ struct CameraOptions
float zFar = 100.f; float zFar = 100.f;
}; };
struct SceneRendererInitArgs
{
sdlpp::GPUTextureFormat colorBufferFormat = sdlpp::GPUTextureFormat::INVALID;
};
struct SceneRendererRenderArgs struct SceneRendererRenderArgs
{ {
CameraOptions camera; CameraOptions camera;
@ -78,7 +83,7 @@ private:
sdlpp::GPUGraphicsPipeline mPipeline; sdlpp::GPUGraphicsPipeline mPipeline;
sdlpp::GPUSampler mSampler; sdlpp::GPUSampler mSampler;
public: public:
void init(Application& application); void init(Application& application, SceneRendererInitArgs args = {});
[[nodiscard]] [[nodiscard]]
std::shared_ptr<SceneMesh> getMesh(const std::string& resourcePath); std::shared_ptr<SceneMesh> getMesh(const std::string& resourcePath);
@ -93,7 +98,7 @@ public:
void render(const SceneRendererRenderArgs& args); void render(const SceneRendererRenderArgs& args);
private: private:
void createPipeline(); void createPipeline(const SceneRendererInitArgs& args);
}; };
} // namespace sdl_gpu_test } // namespace sdl_gpu_test

View File

@ -475,6 +475,26 @@ struct GPUDepthStencilTargetInfo
static_assert(sizeof(GPUDepthStencilTargetInfo) == sizeof(SDL_GPUDepthStencilTargetInfo) static_assert(sizeof(GPUDepthStencilTargetInfo) == sizeof(SDL_GPUDepthStencilTargetInfo)
&& alignof(GPUDepthStencilTargetInfo) == alignof(SDL_GPUDepthStencilTargetInfo)); && alignof(GPUDepthStencilTargetInfo) == alignof(SDL_GPUDepthStencilTargetInfo));
struct GPUStorageTextureWriteOnlyBinding
{
SDL_GPUTexture* texture = nullptr;
Uint32 mipLevel = 0;
Uint32 layer = 0;
bool cycle = false;
std::array<Uint8, 3> padding_;
};
static_assert(sizeof(GPUStorageTextureWriteOnlyBinding) == sizeof(SDL_GPUStorageTextureWriteOnlyBinding)
&& alignof(GPUStorageTextureWriteOnlyBinding) == alignof(SDL_GPUStorageTextureWriteOnlyBinding));
struct GPUStorageBufferWriteOnlyBinding
{
SDL_GPUBuffer* buffer = nullptr;
bool cycle = false;
std::array<Uint8, 3> padding;
};
static_assert(sizeof(GPUStorageBufferWriteOnlyBinding) == sizeof(SDL_GPUStorageBufferWriteOnlyBinding)
&& alignof(GPUStorageBufferWriteOnlyBinding) == alignof(SDL_GPUStorageBufferWriteOnlyBinding));
struct GPUTransferBufferLocation struct GPUTransferBufferLocation
{ {
SDL_GPUTransferBuffer* transferBuffer; SDL_GPUTransferBuffer* transferBuffer;
@ -513,6 +533,17 @@ struct GPUTextureRegion
}; };
static_assert(sizeof(GPUTextureRegion) == sizeof(SDL_GPUTextureRegion)); static_assert(sizeof(GPUTextureRegion) == sizeof(SDL_GPUTextureRegion));
struct GPUTextureLocation
{
SDL_GPUTexture* texture = nullptr;
Uint32 mipLevel = 0;
Uint32 layer = 0;
Uint32 x = 0;
Uint32 y = 0;
Uint32 z = 0;
};
static_assert(sizeof(GPUTextureLocation) == sizeof(SDL_GPUTextureLocation));
using GPUBufferBinding = SDL_GPUBufferBinding; using GPUBufferBinding = SDL_GPUBufferBinding;
using GPUTextureSamplerBinding = SDL_GPUTextureSamplerBinding; using GPUTextureSamplerBinding = SDL_GPUTextureSamplerBinding;
@ -598,6 +629,60 @@ public:
friend class GPUCommandBuffer; friend class GPUCommandBuffer;
}; };
class GPUComputePass : public Base<SDL_GPUComputePass, GPUComputePass>
{
public:
GPUComputePass() noexcept = default;
GPUComputePass(const GPUComputePass&) = delete;
GPUComputePass(GPUComputePass&& other) noexcept : Base(std::move(other)) {}
GPUComputePass& operator=(const GPUComputePass&) = delete;
GPUComputePass& operator=(GPUComputePass&& other) noexcept
{
Base::operator=(std::move(other));
return *this;
}
auto operator<=>(const GPUComputePass& other) const noexcept = default;
void end() noexcept
{
SDL_EndGPUComputePass(mHandle);
mHandle = nullptr;
}
void destroy() noexcept
{
MIJIN_ASSERT(mHandle == nullptr, "Computepass has not been ended.");
}
void bindComputePipeline(SDL_GPUComputePipeline* computePipeline) const noexcept
{
SDL_BindGPUComputePipeline(mHandle, computePipeline);
}
void bindStorageTextures(Uint32 slotIndex, std::span<SDL_GPUTexture* const> storageTextures) const noexcept
{
SDL_BindGPUComputeStorageTextures(
/* compute_pass = */ mHandle,
/* first_slot = */ slotIndex,
/* storage_textures = */ storageTextures.data(),
/* num_bindings = */ static_cast<Uint32>(storageTextures.size())
);
}
void bindStorageTexture(Uint32 slotIndex, SDL_GPUTexture* storageTexture) const noexcept
{
bindStorageTextures(slotIndex, {&storageTexture, 1});
}
void dispatch(Uint32 groupCountX, Uint32 groupCountY = 1, Uint32 groupCountZ = 1) const noexcept
{
SDL_DispatchGPUCompute(mHandle, groupCountX, groupCountY, groupCountZ);
}
friend class GPUCommandBuffer;
};
class GPUCopyPass : public Base<SDL_GPUCopyPass, GPUCopyPass> class GPUCopyPass : public Base<SDL_GPUCopyPass, GPUCopyPass>
{ {
public: public:
@ -644,6 +729,20 @@ public:
); );
} }
void copyTextureToTexture(const GPUTextureLocation& source, const GPUTextureLocation& destination, Uint32 width,
Uint32 height, Uint32 depth = 1, bool cycle = false) const noexcept
{
SDL_CopyGPUTextureToTexture(
/* copy_pass = */ mHandle,
/* source = */ std::bit_cast<const SDL_GPUTextureLocation*>(&source),
/* destination = */ std::bit_cast<const SDL_GPUTextureLocation*>(&destination),
/* w = */ width,
/* h = */ height,
/* d = */ depth,
/* cycle = */ cycle
);
}
friend class GPUCommandBuffer; friend class GPUCommandBuffer;
}; };
@ -787,6 +886,12 @@ struct GPUBeginRenderPassArgs
std::optional<GPUDepthStencilTargetInfo> depthStencilTargetInfo; std::optional<GPUDepthStencilTargetInfo> depthStencilTargetInfo;
}; };
struct GPUBeginComputePassArgs
{
std::span<const GPUStorageTextureWriteOnlyBinding> storageTextureBindings;
std::span<const GPUStorageBufferWriteOnlyBinding> storageBufferBindings;
};
class GPUCommandBuffer : public Base<SDL_GPUCommandBuffer, GPUCommandBuffer> class GPUCommandBuffer : public Base<SDL_GPUCommandBuffer, GPUCommandBuffer>
{ {
public: public:
@ -826,6 +931,20 @@ public:
return renderPass; return renderPass;
} }
[[nodiscard]]
GPUComputePass beginComputePass(const GPUBeginComputePassArgs& args) const noexcept
{
GPUComputePass computePass;
computePass.mHandle = SDL_BeginGPUComputePass(
/* command_buffer = */ mHandle,
/* storage_texture_bindings = */ std::bit_cast<const SDL_GPUStorageTextureWriteOnlyBinding*>(args.storageTextureBindings.data()),
/* num_storage_texture_bindings = */ static_cast<Uint32>(args.storageTextureBindings.size()),
/* storage_buffer_bindings = */ std::bit_cast<const SDL_GPUStorageBufferWriteOnlyBinding*>(args.storageBufferBindings.data()),
/* num_storage_buffer_bindings = */ static_cast<Uint32>(args.storageBufferBindings.size())
);
return computePass;
}
[[nodiscard]] [[nodiscard]]
GPUCopyPass beginCopyPass() const noexcept GPUCopyPass beginCopyPass() const noexcept
{ {
@ -1039,6 +1158,73 @@ public:
} }
}; };
struct GPUComputePipelineCreateArgs
{
std::span<const Uint8> code;
std::string entrypoint = "main";
GPUShaderFormat format;
Uint32 numReadonlyStorageTextures = 0;
Uint32 numReadonlyStorageBuffers = 0;
Uint32 numWriteonlyStorageTextures = 0;
Uint32 numWriteonlyStorageBuffers = 0;
Uint32 numUniformBuffers = 0;
Uint32 threadcountX = 1;
Uint32 threadcountY = 1;
Uint32 threadcountZ = 1;
};
class GPUComputePipeline : public BaseWithDevice<SDL_GPUComputePipeline, GPUComputePipeline>
{
public:
GPUComputePipeline() noexcept = default;
GPUComputePipeline(const GPUComputePipeline&) = delete;
GPUComputePipeline(GPUComputePipeline&& other) noexcept : BaseWithDevice(std::move(other)) {}
GPUComputePipeline& operator=(const GPUComputePipeline&) = delete;
GPUComputePipeline& operator=(GPUComputePipeline&& other) noexcept
{
BaseWithDevice::operator=(std::move(other));
return *this;
}
auto operator<=>(const GPUComputePipeline& other) const noexcept = default;
void create(SDL_GPUDevice* device, const GPUComputePipelineCreateArgs& args)
{
MIJIN_ASSERT(mHandle == nullptr, "GPUComputePipeline has already been created.");
const SDL_GPUComputePipelineCreateInfo createInfo =
{
.code_size = args.code.size(),
.code = args.code.data(),
.entrypoint = args.entrypoint.c_str(),
.format = static_cast<SDL_GPUShaderFormat>(args.format),
.num_readonly_storage_textures = args.numReadonlyStorageTextures,
.num_readonly_storage_buffers = args.numReadonlyStorageBuffers,
.num_writeonly_storage_textures = args.numWriteonlyStorageTextures,
.num_writeonly_storage_buffers = args.numWriteonlyStorageBuffers,
.threadcount_x = args.threadcountX,
.threadcount_y = args.threadcountY,
.threadcount_z = args.threadcountZ,
.props = 0
};
mHandle = SDL_CreateGPUComputePipeline(device, &createInfo);
if (mHandle == nullptr)
{
throw SDLError();
}
mDevice = device;
}
void destroy() noexcept
{
if (mHandle != nullptr)
{
SDL_ReleaseGPUComputePipeline(mDevice, mHandle);
mDevice = nullptr;
mHandle = nullptr;
}
}
};
struct GPUShaderCreateArgs struct GPUShaderCreateArgs
{ {
std::span<const Uint8> code; std::span<const Uint8> code;