Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
World3DPipeline.cpp
Go to the documentation of this file.
1#include "World3DPipeline.hpp"
2
4
5#include <SDL3/SDL.h>
6
7#include <array>
8#include <cstddef>
9#include <utility>
10
11namespace monopoly::engine
12{
13 std::optional<SDL_GPUTextureFormat> chooseWorld3DDepthFormat(
14 SDL_GPUDevice* device) noexcept
15 {
16 if (!device) return std::nullopt;
17 constexpr std::array candidates{
22 };
23 for (const auto format : candidates)
24 {
28 return format;
29 }
30 return std::nullopt;
31 }
32
38 {
39 *this = std::move(other);
40 }
41
43 {
44 if (this == &other) return *this;
45 reset();
46 device_ = std::exchange(other.device_, nullptr);
47 pipeline_ = std::exchange(other.pipeline_, nullptr);
48 shadowPipeline_ = std::exchange(other.shadowPipeline_, nullptr);
49 doubleSidedPipeline_ = std::exchange(other.doubleSidedPipeline_, nullptr);
50 depthFormat_ = std::exchange(other.depthFormat_, SDL_GPU_TEXTUREFORMAT_INVALID);
51 shaders_ = std::move(other.shaders_);
52 return *this;
53 }
54
56 {
57 if (device_ && doubleSidedPipeline_)
58 SDL_ReleaseGPUGraphicsPipeline(device_, doubleSidedPipeline_);
59 if (device_ && shadowPipeline_)
60 SDL_ReleaseGPUGraphicsPipeline(device_, shadowPipeline_);
61 if (device_ && pipeline_)
62 SDL_ReleaseGPUGraphicsPipeline(device_, pipeline_);
63 shadowPipeline_ = nullptr;
64 doubleSidedPipeline_ = nullptr;
65 pipeline_ = nullptr;
66 shaders_.reset();
67 depthFormat_ = SDL_GPU_TEXTUREFORMAT_INVALID;
68 device_ = nullptr;
69 }
70
71 std::expected<World3DPipeline, World3DPipelineError> World3DPipeline::load(
72 SDL_GPUDevice* device,
73 const std::filesystem::path& shaderDirectory,
75 {
76 if (!device)
77 return std::unexpected(World3DPipelineError{
78 World3DPipelineErrorCode::MissingDevice, "missing SDL GPU device", {}});
80 return std::unexpected(World3DPipelineError{
82 "invalid World3D color target format", {}});
83
84 const auto depthFormat = chooseWorld3DDepthFormat(device);
85 if (!depthFormat)
86 return std::unexpected(World3DPipelineError{
88 "device exposes no supported World3D depth target", {}});
89
91 if (!shaders)
92 return std::unexpected(World3DPipelineError{
94 shaders.error().detail,
95 shaders.error()});
96
97 static_assert(sizeof(MeshGPUVertex) == sizeof(float) * 12U);
98 static_assert(offsetof(MeshGPUVertex, position) == 0U);
99 static_assert(offsetof(MeshGPUVertex, normal) == sizeof(float) * 3U);
100 static_assert(offsetof(MeshGPUVertex, uv) == sizeof(float) * 6U);
101 static_assert(offsetof(MeshGPUVertex, tangent) == sizeof(float) * 8U);
102
103 const SDL_GPUVertexBufferDescription vertexBuffer{
104 0U,
105 static_cast<Uint32>(sizeof(MeshGPUVertex)),
107 0U
108 };
109 const std::array<SDL_GPUVertexAttribute, 4> attributes{{
111 static_cast<Uint32>(offsetof(MeshGPUVertex, position))},
113 static_cast<Uint32>(offsetof(MeshGPUVertex, normal))},
115 static_cast<Uint32>(offsetof(MeshGPUVertex, uv))},
117 static_cast<Uint32>(offsetof(MeshGPUVertex, tangent))}
118 }};
119
121 colorTarget.format = colorFormat;
122 colorTarget.blend_state.enable_blend = false;
123 colorTarget.blend_state.enable_color_write_mask = false;
124
126 info.vertex_shader = shaders->vertex();
127 info.fragment_shader = shaders->fragment();
128 info.vertex_input_state.vertex_buffer_descriptions = &vertexBuffer;
129 info.vertex_input_state.num_vertex_buffers = 1U;
130 info.vertex_input_state.vertex_attributes = attributes.data();
131 info.vertex_input_state.num_vertex_attributes =
132 modernPBR ? static_cast<Uint32>(attributes.size()) : 3U;
133 info.primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST;
134
135 info.rasterizer_state.fill_mode = SDL_GPU_FILLMODE_FILL;
136 // D3DDevice::SetInitialRenderingStates leaves CULLMODE at the
137 // IDirect3DDevice3 default D3DCULL_CCW: clockwise fronts survive.
138 // The shadow pipeline inherits this same rasterizer state below.
139 info.rasterizer_state.cull_mode = SDL_GPU_CULLMODE_BACK;
140 info.rasterizer_state.front_face = SDL_GPU_FRONTFACE_CLOCKWISE;
141 info.rasterizer_state.enable_depth_clip = true;
142 info.multisample_state.sample_count = samples;
143
144 info.depth_stencil_state.compare_op = SDL_GPU_COMPAREOP_LESS_OR_EQUAL;
145 info.depth_stencil_state.enable_depth_test = true;
146 info.depth_stencil_state.enable_depth_write = true;
147 info.depth_stencil_state.enable_stencil_test = false;
148
149 info.target_info.color_target_descriptions = &colorTarget;
150 info.target_info.num_color_targets = 1U;
151 info.target_info.depth_stencil_format = *depthFormat;
152 info.target_info.has_depth_stencil_target = true;
153
154 SDL_GPUGraphicsPipeline* pipeline =
155 SDL_CreateGPUGraphicsPipeline(device, &info);
156 if (!pipeline)
157 return std::unexpected(World3DPipelineError{
159 SDL_GetError(), {}});
160
161 if (modernPBR)
162 {
163 info.rasterizer_state.cull_mode = SDL_GPU_CULLMODE_NONE;
164 SDL_GPUGraphicsPipeline* doubleSided =
165 SDL_CreateGPUGraphicsPipeline(device, &info);
166 if (!doubleSided)
167 {
168 SDL_ReleaseGPUGraphicsPipeline(device, pipeline);
169 return std::unexpected(World3DPipelineError{
171 SDL_GetError(), {}});
172 }
173 World3DPipeline result;
174 result.device_ = device;
175 result.pipeline_ = pipeline;
176 result.doubleSidedPipeline_ = doubleSided;
177 result.depthFormat_ = *depthFormat;
178 result.shaders_ = std::move(*shaders);
179 return result;
180 }
181
182 // UDPieces.cpp + UDUTILS_ConvertToShadow use D3DBLEND_ZERO /
183 // D3DBLEND_SRCALPHA for the eleven token-shadow meshes. Their texture
184 // stores transmissivity in alpha: black texels darken the destination,
185 // white texels leave it untouched.
186 colorTarget.blend_state.enable_blend = true;
187 colorTarget.blend_state.src_color_blendfactor = SDL_GPU_BLENDFACTOR_ZERO;
188 colorTarget.blend_state.dst_color_blendfactor = SDL_GPU_BLENDFACTOR_SRC_ALPHA;
189 colorTarget.blend_state.color_blend_op = SDL_GPU_BLENDOP_ADD;
190 colorTarget.blend_state.src_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ZERO;
191 colorTarget.blend_state.dst_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE;
192 colorTarget.blend_state.alpha_blend_op = SDL_GPU_BLENDOP_ADD;
193
195 SDL_CreateGPUGraphicsPipeline(device, &info);
196 if (!shadowPipeline)
197 {
198 SDL_ReleaseGPUGraphicsPipeline(device, pipeline);
199 return std::unexpected(World3DPipelineError{
201 SDL_GetError(), {}});
202 }
203
204 World3DPipeline result;
205 result.device_ = device;
206 result.pipeline_ = pipeline;
207 result.shadowPipeline_ = shadowPipeline;
208 result.depthFormat_ = *depthFormat;
209 result.shaders_ = std::move(*shaders);
210 return result;
211 }
212}
unsigned index
CardTag other
SDL_GPUShaderFormat format
World3DPipeline & operator=(const World3DPipeline &)=delete
SDL_GPUTextureFormat depthFormat() const noexcept
const World3DShaderSet & shaders() const noexcept
static std::expected< World3DPipeline, World3DPipelineError > load(SDL_GPUDevice *device, const std::filesystem::path &shaderDirectory, SDL_GPUTextureFormat colorFormat, bool modernPBR=false, SDL_GPUSampleCount samples=SDL_GPU_SAMPLECOUNT_1)
static std::expected< World3DShaderSet, World3DShaderError > load(SDL_GPUDevice *device, const std::filesystem::path &shaderDirectory, bool modernPBR=false)
SDL_GPUShader * vertex() const noexcept
SDL_GPUShader * fragment() const noexcept
std::optional< SDL_GPUTextureFormat > chooseWorld3DDepthFormat(SDL_GPUDevice *device) noexcept