Added code to create shaders and graphics pipelines.
This commit is contained in:
parent
7b19170112
commit
cd4ab6147a
2
external/scons-plus-plus
vendored
2
external/scons-plus-plus
vendored
@ -1 +1 @@
|
||||
Subproject commit 941f94a7b6ad9242ee1404663dfd855dcf203817
|
||||
Subproject commit fe8f329b3852b0492cee6fc3c7d5ed71cf761c07
|
149
private/sdl_gpu_test/DirStackFileIncluder.h
Normal file
149
private/sdl_gpu_test/DirStackFileIncluder.h
Normal file
@ -0,0 +1,149 @@
|
||||
//
|
||||
// Copyright (C) 2002-2005 3Dlabs Inc. Ltd.
|
||||
// Copyright (C) 2017 Google, Inc.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
//
|
||||
// Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
//
|
||||
// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
#include "./../glslang/Public/ShaderLang.h"
|
||||
|
||||
// Default include class for normal include convention of search backward
|
||||
// through the stack of active include paths (for nested includes).
|
||||
// Can be overridden to customize.
|
||||
class DirStackFileIncluder : public glslang::TShader::Includer {
|
||||
public:
|
||||
DirStackFileIncluder() : externalLocalDirectoryCount(0) { }
|
||||
|
||||
virtual IncludeResult* includeLocal(const char* headerName,
|
||||
const char* includerName,
|
||||
size_t inclusionDepth) override
|
||||
{
|
||||
return readLocalPath(headerName, includerName, (int)inclusionDepth);
|
||||
}
|
||||
|
||||
virtual IncludeResult* includeSystem(const char* headerName,
|
||||
const char* /*includerName*/,
|
||||
size_t /*inclusionDepth*/) override
|
||||
{
|
||||
return readSystemPath(headerName);
|
||||
}
|
||||
|
||||
// Externally set directories. E.g., from a command-line -I<dir>.
|
||||
// - Most-recently pushed are checked first.
|
||||
// - All these are checked after the parse-time stack of local directories
|
||||
// is checked.
|
||||
// - This only applies to the "local" form of #include.
|
||||
// - Makes its own copy of the path.
|
||||
virtual void pushExternalLocalDirectory(const std::string& dir)
|
||||
{
|
||||
directoryStack.push_back(dir);
|
||||
externalLocalDirectoryCount = (int)directoryStack.size();
|
||||
}
|
||||
|
||||
virtual void releaseInclude(IncludeResult* result) override
|
||||
{
|
||||
if (result != nullptr) {
|
||||
delete [] static_cast<tUserDataElement*>(result->userData);
|
||||
delete result;
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::set<std::string> getIncludedFiles()
|
||||
{
|
||||
return includedFiles;
|
||||
}
|
||||
|
||||
virtual ~DirStackFileIncluder() override { }
|
||||
|
||||
protected:
|
||||
typedef char tUserDataElement;
|
||||
std::vector<std::string> directoryStack;
|
||||
int externalLocalDirectoryCount;
|
||||
std::set<std::string> includedFiles;
|
||||
|
||||
// Search for a valid "local" path based on combining the stack of include
|
||||
// directories and the nominal name of the header.
|
||||
virtual IncludeResult* readLocalPath(const char* headerName, const char* includerName, int depth)
|
||||
{
|
||||
// Discard popped include directories, and
|
||||
// initialize when at parse-time first level.
|
||||
directoryStack.resize(depth + externalLocalDirectoryCount);
|
||||
if (depth == 1)
|
||||
directoryStack.back() = getDirectory(includerName);
|
||||
|
||||
// Find a directory that works, using a reverse search of the include stack.
|
||||
for (auto it = directoryStack.rbegin(); it != directoryStack.rend(); ++it) {
|
||||
std::string path = *it + '/' + headerName;
|
||||
std::replace(path.begin(), path.end(), '\\', '/');
|
||||
std::ifstream file(path, std::ios_base::binary | std::ios_base::ate);
|
||||
if (file) {
|
||||
directoryStack.push_back(getDirectory(path));
|
||||
includedFiles.insert(path);
|
||||
return newIncludeResult(path, file, (int)file.tellg());
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Search for a valid <system> path.
|
||||
// Not implemented yet; returning nullptr signals failure to find.
|
||||
virtual IncludeResult* readSystemPath(const char* /*headerName*/) const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Do actual reading of the file, filling in a new include result.
|
||||
virtual IncludeResult* newIncludeResult(const std::string& path, std::ifstream& file, int length) const
|
||||
{
|
||||
char* content = new tUserDataElement [length];
|
||||
file.seekg(0, file.beg);
|
||||
file.read(content, length);
|
||||
return new IncludeResult(path, content, length, content);
|
||||
}
|
||||
|
||||
// If no path markers, return current working directory.
|
||||
// Otherwise, strip file name and return path leading up to it.
|
||||
virtual std::string getDirectory(const std::string path) const
|
||||
{
|
||||
size_t last = path.find_last_of("/\\");
|
||||
return last == std::string::npos ? "." : path.substr(0, last);
|
||||
}
|
||||
};
|
@ -3,6 +3,8 @@ Import('env')
|
||||
|
||||
src_files = Split("""
|
||||
main.cpp
|
||||
|
||||
glsl_compiler.cpp
|
||||
""")
|
||||
|
||||
prog_app = env.UnityProgram(
|
||||
@ -15,7 +17,8 @@ prog_app = env.UnityProgram(
|
||||
'ref': '76ce83801ade3ac922ad5ba6fddc49764c24206a'
|
||||
}
|
||||
},
|
||||
'spdlog': {}
|
||||
'spdlog': {},
|
||||
'glslang': {}
|
||||
}
|
||||
)
|
||||
env.Default(prog_app)
|
||||
|
133
private/sdl_gpu_test/glsl_compiler.cpp
Normal file
133
private/sdl_gpu_test/glsl_compiler.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
|
||||
#include "./glsl_compiler.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <glslang/Public/ResourceLimits.h>
|
||||
#include <glslang/Public/ShaderLang.h>
|
||||
#include <glslang/SPIRV/GlslangToSpv.h>
|
||||
#include <mijin/debug/assert.hpp>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <mijin/util/winundef.hpp>
|
||||
|
||||
#include "./DirStackFileIncluder.h"
|
||||
|
||||
namespace sdl_gpu_test
|
||||
{
|
||||
namespace
|
||||
{
|
||||
EShLanguage mapShaderStage(ShaderStage stage) noexcept
|
||||
{
|
||||
switch (stage)
|
||||
{
|
||||
case ShaderStage::VERTEX:
|
||||
return EShLangVertex;
|
||||
case ShaderStage::FRAGMENT:
|
||||
return EShLangFragment;
|
||||
}
|
||||
MIJIN_FATAL("Invalid value for stage.");
|
||||
}
|
||||
}
|
||||
|
||||
void initGLSLCompiler()
|
||||
{
|
||||
if (!glslang::InitializeProcess())
|
||||
{
|
||||
throw std::runtime_error("Error initializing Glslang.");
|
||||
}
|
||||
}
|
||||
|
||||
void cleanupGLSLCompiler() noexcept
|
||||
{
|
||||
glslang::FinalizeProcess();
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::vector<std::uint32_t> compileGLSL(std::string_view source, const CompileGLSLArgs& args)
|
||||
{
|
||||
const EShLanguage stage = mapShaderStage(args.stage);
|
||||
std::unique_ptr<glslang::TShader> shader = std::make_unique<glslang::TShader>(stage);
|
||||
const char* sourcePtr = source.data();
|
||||
int sourceLength = static_cast<int>(source.size());
|
||||
shader->setStringsWithLengths(&sourcePtr, &sourceLength, 1);
|
||||
shader->setDebugInfo(true);
|
||||
shader->setEnvInput(glslang::EShSourceGlsl, stage, glslang::EShClientVulkan, glslang::EShTargetVulkan_1_3);
|
||||
shader->setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_3);
|
||||
shader->setEnvTarget(glslang::EShTargetLanguage::EshTargetSpv, glslang::EShTargetSpv_1_6);
|
||||
|
||||
DirStackFileIncluder includer;
|
||||
std::string preprocessedCode;
|
||||
const bool couldPreprocess = shader->preprocess(
|
||||
/* builtinResources = */ GetDefaultResources(),
|
||||
/* defaultVersion = */ 450,
|
||||
/* defaultProfile = */ ECoreProfile,
|
||||
/* forceDefaultVersionAndProfile = */ false,
|
||||
/* forwardCompatible = */ false,
|
||||
/* message = */ static_cast<EShMessages>(EShMsgDefault | EShMsgDebugInfo),
|
||||
/* outputString = */ &preprocessedCode,
|
||||
/* includer = */ includer
|
||||
);
|
||||
if (!couldPreprocess)
|
||||
{
|
||||
spdlog::error("GLSL preprocessing failed:\ninfo log:\n{}\ndebug log:\n{}",
|
||||
shader->getInfoLog(), shader->getInfoDebugLog()
|
||||
);
|
||||
throw std::runtime_error("Error preprocessing GLSL.");
|
||||
}
|
||||
sourcePtr = preprocessedCode.c_str();
|
||||
shader->setStrings(&sourcePtr, 1);
|
||||
|
||||
const bool couldParse = shader->parse(
|
||||
/* builtinResources = */ GetDefaultResources(),
|
||||
/* defaultVersion = */ 450,
|
||||
/* forwardCompatible = */ false,
|
||||
/* messages = */ static_cast<EShMessages>(EShMsgDefault | EShMsgDebugInfo)
|
||||
);
|
||||
if (!couldParse)
|
||||
{
|
||||
spdlog::error("GLSL parsing failed:\ninfo log:\n{}\ndebug log:\n{}",
|
||||
shader->getInfoLog(), shader->getInfoDebugLog()
|
||||
);
|
||||
throw std::runtime_error("Error parsing GLSL.");
|
||||
}
|
||||
|
||||
std::unique_ptr<glslang::TProgram> program = std::make_unique<glslang::TProgram>();
|
||||
program->addShader(shader.get());
|
||||
|
||||
if (!program->link(static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules | EShMsgDebugInfo)))
|
||||
{
|
||||
spdlog::error("GLSL compilation failed:\ninfo log:\n{}\ndebug log:\n{}",
|
||||
program->getInfoLog(), program->getInfoDebugLog()
|
||||
);
|
||||
throw std::runtime_error("Error linking GLSL.");
|
||||
}
|
||||
|
||||
glslang::SpvOptions spvOptions =
|
||||
{
|
||||
.generateDebugInfo = true,
|
||||
.stripDebugInfo = false,
|
||||
.disableOptimizer = true,
|
||||
.optimizeSize = false,
|
||||
.disassemble = false,
|
||||
.validate = true,
|
||||
.emitNonSemanticShaderDebugInfo = true,
|
||||
.emitNonSemanticShaderDebugSource = false, // maybe?
|
||||
.compileOnly = false
|
||||
};
|
||||
spv::SpvBuildLogger logger;
|
||||
const glslang::TIntermediate* intermediate = program->getIntermediate(stage);
|
||||
std::vector<std::uint32_t> spirv;
|
||||
glslang::GlslangToSpv(*intermediate, spirv, &logger, &spvOptions);
|
||||
|
||||
const std::string messages = logger.getAllMessages();
|
||||
if (!messages.empty())
|
||||
{
|
||||
spdlog::warn("SpirV messages: {}", messages);
|
||||
}
|
||||
if (spirv.empty())
|
||||
{
|
||||
throw std::runtime_error("Error generating SpirV.");
|
||||
}
|
||||
return spirv;
|
||||
}
|
||||
}
|
30
private/sdl_gpu_test/glsl_compiler.hpp
Normal file
30
private/sdl_gpu_test/glsl_compiler.hpp
Normal file
@ -0,0 +1,30 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_GLSL_COMPILER_HPP_INCLUDED)
|
||||
#define SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_GLSL_COMPILER_HPP_INCLUDED 1
|
||||
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace sdl_gpu_test
|
||||
{
|
||||
enum class ShaderStage
|
||||
{
|
||||
VERTEX,
|
||||
FRAGMENT
|
||||
};
|
||||
|
||||
struct CompileGLSLArgs
|
||||
{
|
||||
ShaderStage stage;
|
||||
};
|
||||
|
||||
void initGLSLCompiler();
|
||||
void cleanupGLSLCompiler() noexcept;
|
||||
|
||||
[[nodiscard]]
|
||||
std::vector<std::uint32_t> compileGLSL(std::string_view source, const CompileGLSLArgs& args);
|
||||
} // namespace sdl_gpu_test
|
||||
|
||||
#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_GLSL_COMPILER_HPP_INCLUDED)
|
@ -4,12 +4,37 @@
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <mijin/util/winundef.hpp>
|
||||
|
||||
#include "./glsl_compiler.hpp"
|
||||
#include "./sdlpp/event.hpp"
|
||||
#include "./sdlpp/gpu.hpp"
|
||||
#include "./sdlpp/window.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
const char* const VERTEX_SOURCE = R"(
|
||||
#version 460
|
||||
|
||||
void main()
|
||||
{
|
||||
|
||||
}
|
||||
)";
|
||||
|
||||
const char* const FRAGMENT_SOURCE = R"(
|
||||
#version 460
|
||||
|
||||
void main()
|
||||
{
|
||||
|
||||
}
|
||||
)";
|
||||
}
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
using namespace sdl_gpu_test;
|
||||
|
||||
// init SDL
|
||||
if (SDL_Init(0) != SDL_TRUE)
|
||||
{
|
||||
spdlog::error("Error initializing SDL.");
|
||||
@ -19,6 +44,12 @@ int main(int, char**)
|
||||
SDL_Quit();
|
||||
};
|
||||
|
||||
// init glslang
|
||||
initGLSLCompiler();
|
||||
MIJIN_SCOPE_EXIT {
|
||||
cleanupGLSLCompiler();
|
||||
};
|
||||
|
||||
sdlpp::Window window;
|
||||
window.create({
|
||||
.flags = {.vulkan = true}
|
||||
@ -29,7 +60,30 @@ int main(int, char**)
|
||||
|
||||
gpuDevice.claimWindow(window);
|
||||
|
||||
// create vertex shader
|
||||
std::vector<std::uint32_t> vertexSpv = compileGLSL(VERTEX_SOURCE, {.stage = ShaderStage::VERTEX});
|
||||
sdlpp::GPUShader vertexShader;
|
||||
vertexShader.create(gpuDevice, {
|
||||
.code = {reinterpret_cast<const Uint8*>(vertexSpv.data()), vertexSpv.size() * sizeof(std::uint32_t)},
|
||||
.format = sdlpp::GPUShaderFormat::SPIRV,
|
||||
.stage = sdlpp::GPUShaderStage::VERTEX
|
||||
});
|
||||
|
||||
// create fragment shader
|
||||
std::vector<std::uint32_t> fragmentSpv = compileGLSL(FRAGMENT_SOURCE, {.stage = ShaderStage::FRAGMENT});
|
||||
sdlpp::GPUShader fragmentShader;
|
||||
fragmentShader.create(gpuDevice, {
|
||||
.code = {reinterpret_cast<const Uint8*>(fragmentSpv.data()), fragmentSpv.size() * sizeof(std::uint32_t)},
|
||||
.format = sdlpp::GPUShaderFormat::SPIRV,
|
||||
.stage = sdlpp::GPUShaderStage::FRAGMENT
|
||||
});
|
||||
|
||||
// create graphics pipeline
|
||||
sdlpp::GPUGraphicsPipeline pipeline;
|
||||
pipeline.create(gpuDevice, {
|
||||
.vertexShader = vertexShader,
|
||||
.fragmentShader = fragmentShader
|
||||
});
|
||||
|
||||
bool running = true;
|
||||
while(running)
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include <mijin/debug/assert.hpp>
|
||||
|
@ -8,87 +8,15 @@
|
||||
|
||||
namespace sdlpp
|
||||
{
|
||||
struct GpuShaderFormat : mijin::BitFlags<GpuShaderFormat>
|
||||
{
|
||||
bool private_ : 1 = false;
|
||||
bool spirv : 1 = false;
|
||||
bool dxbc : 1 = false;
|
||||
bool dxil : 1 = false;
|
||||
bool msl : 1 = false;
|
||||
bool metallib : 1 = false;
|
||||
|
||||
constexpr operator SDL_GPUShaderFormat() const noexcept
|
||||
{
|
||||
return std::bit_cast<std::uint8_t>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
struct GPUDeviceCreateArgs
|
||||
{
|
||||
GpuShaderFormat formatFlags = {};
|
||||
bool debugMode = false;
|
||||
const char* name = nullptr;
|
||||
};
|
||||
|
||||
class GPUDevice : public Base<SDL_GPUDevice, GPUDevice>
|
||||
{
|
||||
public:
|
||||
GPUDevice() noexcept = default;
|
||||
GPUDevice(const GPUDevice&) = delete;
|
||||
GPUDevice(GPUDevice&& other) noexcept : Base(std::move(other)) {}
|
||||
|
||||
GPUDevice& operator=(const GPUDevice&) = delete;
|
||||
GPUDevice& operator=(GPUDevice&& other) noexcept
|
||||
{
|
||||
Base::operator=(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
auto operator<=>(const GPUDevice& other) const noexcept = default;
|
||||
|
||||
void create(const GPUDeviceCreateArgs& args = {})
|
||||
{
|
||||
MIJIN_ASSERT(mHandle == nullptr, "GPUDevice has already been created.");
|
||||
mHandle = SDL_CreateGPUDevice(args.formatFlags, args.debugMode, args.name);
|
||||
if (mHandle == nullptr)
|
||||
{
|
||||
throw SDLError();
|
||||
}
|
||||
}
|
||||
|
||||
void destroy() noexcept
|
||||
{
|
||||
if (mHandle != nullptr)
|
||||
{
|
||||
SDL_DestroyGPUDevice(mHandle);
|
||||
mHandle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void claimWindow(SDL_Window* window) const
|
||||
{
|
||||
if (!SDL_ClaimWindowForGPUDevice(*this, window))
|
||||
{
|
||||
throw SDLError();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// enums
|
||||
//
|
||||
enum class GPUVertexInputRate
|
||||
{
|
||||
VERTEX = SDL_GPU_VERTEXINPUTRATE_VERTEX,
|
||||
INSTANCE = SDL_GPU_VERTEXINPUTRATE_INSTANCE
|
||||
};
|
||||
|
||||
struct GPUVertexBinding
|
||||
{
|
||||
Uint32 index;
|
||||
Uint32 pitch;
|
||||
GPUVertexInputRate inputRate = GPUVertexInputRate::VERTEX;
|
||||
Uint32 instanceStepRate;
|
||||
};
|
||||
static_assert(sizeof(GPUVertexBinding) == sizeof(SDL_GPUVertexBinding)
|
||||
&& alignof(GPUVertexBinding) == alignof(SDL_GPUVertexBinding));
|
||||
|
||||
enum class GPUVertexElementFormat
|
||||
{
|
||||
INT = SDL_GPU_VERTEXELEMENTFORMAT_INT,
|
||||
@ -123,22 +51,6 @@ enum class GPUVertexElementFormat
|
||||
HALF4 = SDL_GPU_VERTEXELEMENTFORMAT_HALF4
|
||||
};
|
||||
|
||||
struct GPUVertexAttribute
|
||||
{
|
||||
Uint32 location;
|
||||
Uint32 bindingIndex;
|
||||
GPUVertexElementFormat format;
|
||||
Uint32 offset;
|
||||
};
|
||||
static_assert(sizeof(GPUVertexAttribute) == sizeof(SDL_GPUVertexAttribute)
|
||||
&& alignof(GPUVertexAttribute) == alignof(SDL_GPUVertexAttribute));
|
||||
|
||||
struct GPUVertexInputState
|
||||
{
|
||||
std::vector<GPUVertexBinding> vertexBindings;
|
||||
std::vector<GPUVertexAttribute> vertexAttributes;
|
||||
};
|
||||
|
||||
enum class GPUPrimitiveType
|
||||
{
|
||||
POINTLIST = SDL_GPU_PRIMITIVETYPE_POINTLIST,
|
||||
@ -167,17 +79,6 @@ enum class GPUFrontFace
|
||||
CLOCKWISE = SDL_GPU_FRONTFACE_CLOCKWISE
|
||||
};
|
||||
|
||||
struct GPURasterizerState
|
||||
{
|
||||
GPUFillMode fillMode = GPUFillMode::FILL;
|
||||
GPUCullMode cullMode = GPUCullMode::NONE;
|
||||
GPUFrontFace frontFace = GPUFrontFace::COUNTER_CLOCKWISE;
|
||||
bool enableDepthBias = false;
|
||||
float depthBiasConstantFactor;
|
||||
float depthBiasClamp;
|
||||
float depthBiasSlopeFactor;
|
||||
};
|
||||
|
||||
enum class GPUSampleCount
|
||||
{
|
||||
ONE = SDL_GPU_SAMPLECOUNT_1,
|
||||
@ -186,12 +87,6 @@ enum class GPUSampleCount
|
||||
EIGHT = SDL_GPU_SAMPLECOUNT_8
|
||||
};
|
||||
|
||||
struct GPUMultisampleState
|
||||
{
|
||||
GPUSampleCount sampleCount = GPUSampleCount::ONE;
|
||||
Uint32 sampleMask = 0xFFFFFFFF;
|
||||
};
|
||||
|
||||
enum class GPUCompareOp
|
||||
{
|
||||
NEVER = SDL_GPU_COMPAREOP_NEVER,
|
||||
@ -216,36 +111,6 @@ enum class GPUStencilOp
|
||||
DECREMENT_AND_WRAP = SDL_GPU_STENCILOP_DECREMENT_AND_WRAP
|
||||
};
|
||||
|
||||
struct GPUStencilOpState
|
||||
{
|
||||
GPUStencilOp failOp;
|
||||
GPUStencilOp passOp;
|
||||
GPUStencilOp depthFailOp;
|
||||
GPUCompareOp compareOp;
|
||||
|
||||
explicit operator SDL_GPUStencilOpState() const noexcept
|
||||
{
|
||||
return {
|
||||
.fail_op = static_cast<SDL_GPUStencilOp>(failOp),
|
||||
.pass_op = static_cast<SDL_GPUStencilOp>(passOp),
|
||||
.depth_fail_op = static_cast<SDL_GPUStencilOp>(depthFailOp),
|
||||
.compare_op = static_cast<SDL_GPUCompareOp>(compareOp),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct GPUDepthStencilState
|
||||
{
|
||||
bool enableDepthTest = false;
|
||||
bool enableDepthWrite = false;
|
||||
bool enableStencilTest = false;
|
||||
GPUCompareOp compareOp = GPUCompareOp::LESS_OR_EQUAL;
|
||||
GPUStencilOpState backStencilState;
|
||||
GPUStencilOpState frontStencilState;
|
||||
Uint8 compareMask;
|
||||
Uint8 writeMask;
|
||||
};
|
||||
|
||||
enum class GPUBlendFactor
|
||||
{
|
||||
ZERO = SDL_GPU_BLENDFACTOR_ZERO,
|
||||
@ -272,26 +137,6 @@ enum class GPUBlendOp
|
||||
MAX = SDL_GPU_BLENDOP_MAX
|
||||
};
|
||||
|
||||
struct GPUColorComponentFlags : mijin::BitFlags<GPUColorComponentFlags>
|
||||
{
|
||||
bool r : 1 = false;
|
||||
bool g : 1 = false;
|
||||
bool b : 1 = false;
|
||||
bool a : 1 = false;
|
||||
};
|
||||
|
||||
struct GPUColorTargetBlendState
|
||||
{
|
||||
bool enableBlend = false;
|
||||
GPUBlendFactor srcColorBlendfactor;
|
||||
GPUBlendFactor dstColorBlendfactor;
|
||||
GPUBlendOp colorBlendOp;
|
||||
GPUBlendFactor srcAlphaBlendfactor;
|
||||
GPUBlendFactor dstAlphaBlendfactor;
|
||||
GPUBlendOp alphaBlendOp;
|
||||
GPUColorComponentFlags colorWriteMask = {.r = true, .g = true, .b = true, .a = true};
|
||||
};
|
||||
|
||||
enum class GPUTextureFormat
|
||||
{
|
||||
INVALID = SDL_GPU_TEXTUREFORMAT_INVALID,
|
||||
@ -353,6 +198,136 @@ enum class GPUTextureFormat
|
||||
D32_FLOAT_S8_UINT = SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT
|
||||
};
|
||||
|
||||
enum class GPUShaderFormat
|
||||
{
|
||||
PRIVATE = SDL_GPU_SHADERFORMAT_PRIVATE,
|
||||
SPIRV = SDL_GPU_SHADERFORMAT_SPIRV,
|
||||
DXBC = SDL_GPU_SHADERFORMAT_DXBC,
|
||||
DXIL = SDL_GPU_SHADERFORMAT_DXIL,
|
||||
MSL = SDL_GPU_SHADERFORMAT_MSL,
|
||||
METALLIB = SDL_GPU_SHADERFORMAT_METALLIB
|
||||
};
|
||||
|
||||
enum class GPUShaderStage
|
||||
{
|
||||
VERTEX = SDL_GPU_SHADERSTAGE_VERTEX,
|
||||
FRAGMENT = SDL_GPU_SHADERSTAGE_FRAGMENT
|
||||
};
|
||||
|
||||
//
|
||||
// bitflags
|
||||
//
|
||||
struct GPUShaderFormatFlags : mijin::BitFlags<GPUShaderFormatFlags>
|
||||
{
|
||||
bool private_ : 1 = false;
|
||||
bool spirv : 1 = false;
|
||||
bool dxbc : 1 = false;
|
||||
bool dxil : 1 = false;
|
||||
bool msl : 1 = false;
|
||||
bool metallib : 1 = false;
|
||||
|
||||
constexpr operator SDL_GPUShaderFormat() const noexcept
|
||||
{
|
||||
return std::bit_cast<std::uint8_t>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
struct GPUColorComponentFlags : mijin::BitFlags<GPUColorComponentFlags>
|
||||
{
|
||||
bool r : 1 = false;
|
||||
bool g : 1 = false;
|
||||
bool b : 1 = false;
|
||||
bool a : 1 = false;
|
||||
};
|
||||
|
||||
//
|
||||
// structs
|
||||
//
|
||||
struct GPUVertexBinding
|
||||
{
|
||||
Uint32 index;
|
||||
Uint32 pitch;
|
||||
GPUVertexInputRate inputRate = GPUVertexInputRate::VERTEX;
|
||||
Uint32 instanceStepRate;
|
||||
};
|
||||
static_assert(sizeof(GPUVertexBinding) == sizeof(SDL_GPUVertexBinding)
|
||||
&& alignof(GPUVertexBinding) == alignof(SDL_GPUVertexBinding));
|
||||
|
||||
struct GPUVertexAttribute
|
||||
{
|
||||
Uint32 location;
|
||||
Uint32 bindingIndex;
|
||||
GPUVertexElementFormat format;
|
||||
Uint32 offset;
|
||||
};
|
||||
static_assert(sizeof(GPUVertexAttribute) == sizeof(SDL_GPUVertexAttribute)
|
||||
&& alignof(GPUVertexAttribute) == alignof(SDL_GPUVertexAttribute));
|
||||
|
||||
struct GPUVertexInputState
|
||||
{
|
||||
std::span<const GPUVertexBinding> vertexBindings;
|
||||
std::span<const GPUVertexAttribute> vertexAttributes;
|
||||
};
|
||||
|
||||
struct GPURasterizerState
|
||||
{
|
||||
GPUFillMode fillMode = GPUFillMode::FILL;
|
||||
GPUCullMode cullMode = GPUCullMode::NONE;
|
||||
GPUFrontFace frontFace = GPUFrontFace::COUNTER_CLOCKWISE;
|
||||
bool enableDepthBias = false;
|
||||
float depthBiasConstantFactor;
|
||||
float depthBiasClamp;
|
||||
float depthBiasSlopeFactor;
|
||||
};
|
||||
|
||||
struct GPUMultisampleState
|
||||
{
|
||||
GPUSampleCount sampleCount = GPUSampleCount::ONE;
|
||||
Uint32 sampleMask = 0xFFFFFFFF;
|
||||
};
|
||||
|
||||
struct GPUStencilOpState
|
||||
{
|
||||
GPUStencilOp failOp;
|
||||
GPUStencilOp passOp;
|
||||
GPUStencilOp depthFailOp;
|
||||
GPUCompareOp compareOp;
|
||||
|
||||
explicit operator SDL_GPUStencilOpState() const noexcept
|
||||
{
|
||||
return {
|
||||
.fail_op = static_cast<SDL_GPUStencilOp>(failOp),
|
||||
.pass_op = static_cast<SDL_GPUStencilOp>(passOp),
|
||||
.depth_fail_op = static_cast<SDL_GPUStencilOp>(depthFailOp),
|
||||
.compare_op = static_cast<SDL_GPUCompareOp>(compareOp),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct GPUDepthStencilState
|
||||
{
|
||||
bool enableDepthTest = false;
|
||||
bool enableDepthWrite = false;
|
||||
bool enableStencilTest = false;
|
||||
GPUCompareOp compareOp = GPUCompareOp::LESS_OR_EQUAL;
|
||||
GPUStencilOpState backStencilState;
|
||||
GPUStencilOpState frontStencilState;
|
||||
Uint8 compareMask;
|
||||
Uint8 writeMask;
|
||||
};
|
||||
|
||||
struct GPUColorTargetBlendState
|
||||
{
|
||||
bool enableBlend = false;
|
||||
GPUBlendFactor srcColorBlendfactor;
|
||||
GPUBlendFactor dstColorBlendfactor;
|
||||
GPUBlendOp colorBlendOp;
|
||||
GPUBlendFactor srcAlphaBlendfactor;
|
||||
GPUBlendFactor dstAlphaBlendfactor;
|
||||
GPUBlendOp alphaBlendOp;
|
||||
GPUColorComponentFlags colorWriteMask = {.r = true, .g = true, .b = true, .a = true};
|
||||
};
|
||||
|
||||
struct GPUColorTargetDescription
|
||||
{
|
||||
GPUTextureFormat format = GPUTextureFormat::INVALID;
|
||||
@ -364,7 +339,7 @@ static_assert(sizeof(GPUColorTargetDescription) == sizeof(SDL_GPUColorTargetDesc
|
||||
|
||||
struct GPUGraphicsPipelineTargetInfo
|
||||
{
|
||||
std::vector<GPUColorTargetDescription> colorTargetDescriptions;
|
||||
std::span<const GPUColorTargetDescription> colorTargetDescriptions;
|
||||
bool hasDepthStencilTarget = false;
|
||||
GPUTextureFormat depthStencilFormat = GPUTextureFormat::INVALID;
|
||||
|
||||
@ -379,6 +354,59 @@ struct GPUGraphicsPipelineTargetInfo
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// classes
|
||||
//
|
||||
struct GPUDeviceCreateArgs
|
||||
{
|
||||
GPUShaderFormatFlags formatFlags = {};
|
||||
bool debugMode = false;
|
||||
const char* name = nullptr;
|
||||
};
|
||||
|
||||
class GPUDevice : public Base<SDL_GPUDevice, GPUDevice>
|
||||
{
|
||||
public:
|
||||
GPUDevice() noexcept = default;
|
||||
GPUDevice(const GPUDevice&) = delete;
|
||||
GPUDevice(GPUDevice&& other) noexcept : Base(std::move(other)) {}
|
||||
|
||||
GPUDevice& operator=(const GPUDevice&) = delete;
|
||||
GPUDevice& operator=(GPUDevice&& other) noexcept
|
||||
{
|
||||
Base::operator=(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
auto operator<=>(const GPUDevice& other) const noexcept = default;
|
||||
|
||||
void create(const GPUDeviceCreateArgs& args = {})
|
||||
{
|
||||
MIJIN_ASSERT(mHandle == nullptr, "GPUDevice has already been created.");
|
||||
mHandle = SDL_CreateGPUDevice(args.formatFlags, args.debugMode, args.name);
|
||||
if (mHandle == nullptr)
|
||||
{
|
||||
throw SDLError();
|
||||
}
|
||||
}
|
||||
|
||||
void destroy() noexcept
|
||||
{
|
||||
if (mHandle != nullptr)
|
||||
{
|
||||
SDL_DestroyGPUDevice(mHandle);
|
||||
mHandle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void claimWindow(SDL_Window* window) const
|
||||
{
|
||||
if (!SDL_ClaimWindowForGPUDevice(*this, window))
|
||||
{
|
||||
throw SDLError();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct GPUGraphicsPipelineCreateArgs
|
||||
{
|
||||
SDL_GPUShader* vertexShader;
|
||||
@ -404,6 +432,7 @@ public:
|
||||
GPUGraphicsPipeline& operator=(GPUGraphicsPipeline&& other) noexcept
|
||||
{
|
||||
Base::operator=(std::move(other));
|
||||
mDevice = other.mDevice;
|
||||
return *this;
|
||||
}
|
||||
auto operator<=>(const GPUGraphicsPipeline& other) const noexcept = default;
|
||||
@ -466,6 +495,69 @@ public:
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct GPUShaderCreateArgs
|
||||
{
|
||||
std::span<const Uint8> code;
|
||||
std::string entrypoint = "main";
|
||||
GPUShaderFormat format;
|
||||
GPUShaderStage stage;
|
||||
Uint32 numSamplers = 0;
|
||||
Uint32 numStorageTextures = 0;
|
||||
Uint32 numStorageBuffers = 0;
|
||||
Uint32 numUniformBuffers = 0;
|
||||
};
|
||||
|
||||
class GPUShader : public Base<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& operator=(const GPUShader&) = delete;
|
||||
GPUShader& operator=(GPUShader&& other) noexcept
|
||||
{
|
||||
Base::operator=(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
auto operator<=>(const GPUShader& other) const noexcept = default;
|
||||
|
||||
void create(SDL_GPUDevice* device, const GPUShaderCreateArgs& args)
|
||||
{
|
||||
MIJIN_ASSERT(mHandle == nullptr, "GPUShader has already been created.");
|
||||
const SDL_GPUShaderCreateInfo createInfo =
|
||||
{
|
||||
.code_size = args.code.size(),
|
||||
.code = args.code.data(),
|
||||
.entrypoint = args.entrypoint.c_str(),
|
||||
.format = static_cast<SDL_GPUShaderFormat>(args.format),
|
||||
.stage = static_cast<SDL_GPUShaderStage>(args.stage),
|
||||
.num_samplers = args.numSamplers,
|
||||
.num_storage_textures = args.numStorageTextures,
|
||||
.num_storage_buffers = args.numStorageBuffers,
|
||||
.num_uniform_buffers = args.numUniformBuffers
|
||||
};
|
||||
mHandle = SDL_CreateGPUShader(device, &createInfo);
|
||||
if (mHandle == nullptr)
|
||||
{
|
||||
throw SDLError();
|
||||
}
|
||||
mDevice = device;
|
||||
}
|
||||
|
||||
void destroy() noexcept
|
||||
{
|
||||
if (mHandle != nullptr)
|
||||
{
|
||||
SDL_ReleaseGPUShader(mDevice, mHandle);
|
||||
mHandle = nullptr;
|
||||
mDevice = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace sdlpp
|
||||
|
||||
#endif // !defined(SDL_GPU_TEST_PRIVATE_SDL_GPU_TEST_SDLPP_GPU_HPP_INCLUDED)
|
||||
|
Loading…
x
Reference in New Issue
Block a user