MayaFlux 0.2.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
RenderProcessor.cpp
Go to the documentation of this file.
1#include "RenderProcessor.hpp"
2
7
12
14
15namespace MayaFlux::Buffers {
16
22
23void RenderProcessor::set_fragment_shader(const std::string& fragment_path)
24{
26 m_fragment_shader_id = foundry.load_shader(fragment_path, Portal::Graphics::ShaderStage::FRAGMENT);
28}
29
30void RenderProcessor::set_geometry_shader(const std::string& geometry_path)
31{
33 m_geometry_shader_id = foundry.load_shader(geometry_path, Portal::Graphics::ShaderStage::GEOMETRY);
35}
36
37void RenderProcessor::set_tess_control_shader(const std::string& tess_control_path)
38{
40 m_tess_control_shader_id = foundry.load_shader(tess_control_path, Portal::Graphics::ShaderStage::TESS_CONTROL);
42}
43
44void RenderProcessor::set_tess_eval_shader(const std::string& tess_eval_path)
45{
47 m_tess_eval_shader_id = foundry.load_shader(tess_eval_path, Portal::Graphics::ShaderStage::TESS_EVALUATION);
49}
50
51void RenderProcessor::set_target_window(std::shared_ptr<Core::Window> window)
52{
53 m_target_window = std::move(window);
54}
55
57 uint32_t binding,
58 const std::shared_ptr<Core::VKImage>& texture,
59 vk::Sampler sampler)
60{
61 if (!texture) {
63 "Cannot bind null texture to binding {}", binding);
64 return;
65 }
66
67 if (!sampler) {
69 sampler = loom.get_default_sampler();
70 }
71
72 m_texture_bindings[binding] = { .texture = texture, .sampler = sampler };
73
75
77 foundry.update_descriptor_image(
79 binding,
80 texture->get_image_view(),
81 sampler,
82 vk::ImageLayout::eShaderReadOnlyOptimal);
83 }
84
86 "Bound texture to binding {}", binding);
87}
88
90 const std::string& descriptor_name,
91 const std::shared_ptr<Core::VKImage>& texture,
92 vk::Sampler sampler)
93{
94 auto binding_it = m_config.bindings.find(descriptor_name);
95 if (binding_it == m_config.bindings.end()) {
97 "No binding configured for descriptor '{}'", descriptor_name);
98 return;
99 }
100
101 bind_texture(binding_it->second.binding, texture, sampler);
102}
103
104void RenderProcessor::initialize_pipeline(const std::shared_ptr<VKBuffer>& buffer)
105{
108 "Vertex shader not loaded");
109 return;
110 }
111
114 "Fragment shader not loaded");
115 return;
116 }
117
118 if (!m_target_window) {
120 "Target window not set");
121 return;
122 }
123
125
127
129 pipeline_config.vertex_shader = m_shader_id;
130 pipeline_config.fragment_shader = m_fragment_shader_id;
131 pipeline_config.geometry_shader = m_geometry_shader_id;
133 pipeline_config.tess_eval_shader = m_tess_eval_shader_id;
134
135 pipeline_config.topology = m_primitive_topology;
136 pipeline_config.rasterization.polygon_mode = m_polygon_mode;
137 pipeline_config.rasterization.cull_mode = m_cull_mode;
138
139 pipeline_config.blend_attachments.emplace_back();
140
141 if (m_buffer_info.find(buffer) == m_buffer_info.end()) {
142 if (buffer->has_vertex_layout()) {
143 auto vertex_layout = buffer->get_vertex_layout();
144 if (vertex_layout.has_value()) {
145 m_buffer_info[buffer] = {
146 .semantic_layout = vertex_layout.value(),
147 .use_reflection = false
148 };
149 }
150 }
151 }
152 if (m_buffer_info.find(buffer) != m_buffer_info.end()) {
153 const auto& vertex_info = m_buffer_info.find(buffer)->second;
154 pipeline_config.semantic_vertex_layout = vertex_info.semantic_layout;
155 pipeline_config.use_vertex_shader_reflection = vertex_info.use_reflection;
156 }
157
158 const auto& staging = buffer->get_pipeline_context().push_constant_staging;
159 if (!staging.empty()) {
160 pipeline_config.push_constant_size = staging.size();
161 } else {
162 pipeline_config.push_constant_size = std::max(m_config.push_constant_size, m_push_constant_data.size());
163 }
164
165 auto& descriptor_bindings = buffer->get_pipeline_context().descriptor_buffer_bindings;
166 std::map<std::pair<uint32_t, uint32_t>, Portal::Graphics::DescriptorBindingInfo> unified_bindings;
167
168 for (const auto& binding : descriptor_bindings) {
169 unified_bindings[{ binding.set, binding.binding }] = binding;
170 }
171
172 for (const auto& [name, binding] : m_config.bindings) {
173 auto key = std::make_pair(binding.set, binding.binding);
174 if (unified_bindings.find(key) == unified_bindings.end()) {
175 unified_bindings[key] = Portal::Graphics::DescriptorBindingInfo {
176 .set = binding.set,
177 .binding = binding.binding,
178 .type = binding.type,
179 .buffer_info = {},
180 .name = name
181 };
182 }
183 }
184
185 std::map<uint32_t, std::vector<Portal::Graphics::DescriptorBindingInfo>> bindings_by_set;
186 for (const auto& [key, binding] : unified_bindings) {
187 bindings_by_set[binding.set].push_back(binding);
188 }
189
190 for (const auto& [set_index, set_bindings] : bindings_by_set) {
191 pipeline_config.descriptor_sets.push_back(set_bindings);
192 }
193
194 vk::Format swapchain_format = static_cast<vk::Format>(
196
197 m_pipeline_id = flow.create_pipeline(pipeline_config, { swapchain_format });
198
201 "Failed to create render pipeline");
202 return;
203 }
204
205 m_needs_descriptor_rebuild = !pipeline_config.descriptor_sets.empty() && m_descriptor_set_ids.empty();
207
209}
210
211void RenderProcessor::initialize_descriptors(const std::shared_ptr<VKBuffer>& buffer)
212{
215 "Cannot allocate descriptor sets without pipeline");
216 return;
217 }
218
220
222
223 m_descriptor_set_ids = flow.allocate_pipeline_descriptors(m_pipeline_id);
224
225 if (m_descriptor_set_ids.empty()) {
227 "Failed to allocate descriptor sets for pipeline");
228 return;
229 }
230
231 for (const auto& [binding, tex_binding] : m_texture_bindings) {
232 auto config_it = std::ranges::find_if(m_config.bindings,
233 [binding](const auto& pair) {
234 return pair.second.binding == binding;
235 });
236
237 if (config_it == m_config.bindings.end()) {
239 "No config for binding {}", binding);
240 continue;
241 }
242 uint32_t set_index = config_it->second.set;
243
244 if (set_index >= m_descriptor_set_ids.size()) {
246 "Descriptor set index {} out of range", binding);
247 continue;
248 }
249
250 auto& foundry = Portal::Graphics::get_shader_foundry();
251 foundry.update_descriptor_image(
252 m_descriptor_set_ids[set_index],
253 config_it->second.binding,
254 tex_binding.texture->get_image_view(),
255 tex_binding.sampler,
256 vk::ImageLayout::eShaderReadOnlyOptimal);
257 }
258
260 "Allocated {} descriptor sets and updated {} texture bindings",
262
263 update_descriptors(buffer);
265}
266
267bool RenderProcessor::on_before_execute(Portal::Graphics::CommandBufferID /*cmd_id*/, const std::shared_ptr<VKBuffer>& /*buffer*/)
268{
269 if (!m_target_window) {
271 "Target window not set");
272 return false;
273 }
274 return m_target_window->is_graphics_registered();
275}
276
277void RenderProcessor::execute_shader(const std::shared_ptr<VKBuffer>& buffer)
278{
279 if (m_buffer_info.find(buffer) == m_buffer_info.end()) {
280 if (buffer->has_vertex_layout()) {
281 auto vertex_layout = buffer->get_vertex_layout();
282 if (vertex_layout.has_value()) {
283 m_buffer_info[buffer] = {
284 .semantic_layout = vertex_layout.value(),
285 .use_reflection = false
286 };
287 }
288 }
289 }
290
291 if (!m_target_window->is_graphics_registered()) {
292 return;
293 }
294
296 return;
297 }
298
299 auto vertex_layout = buffer->get_vertex_layout();
300 if (!vertex_layout.has_value()) {
302 "VKBuffer has no vertex layout set. Use buffer->set_vertex_layout()");
303 return;
304 }
305
306 if (vertex_layout->vertex_count == 0) {
308 "Vertex layout has zero vertices, skipping draw");
309 return;
310 }
311
312 if (vertex_layout->attributes.empty()) {
314 "Vertex layout has no attributes");
315 return;
316 }
317
318 buffer->set_pipeline_window(m_pipeline_id, m_target_window);
319
320 auto& foundry = Portal::Graphics::get_shader_foundry();
322
323 vk::Format color_format = static_cast<vk::Format>(
325
326 auto cmd_id = foundry.begin_secondary_commands(color_format);
327 auto cmd = foundry.get_command_buffer(cmd_id);
328
329 uint32_t width = 0, height = 0;
331
332 if (width > 0 && height > 0) {
333 auto cmd = foundry.get_command_buffer(cmd_id);
334
335 vk::Viewport viewport { 0.0F, 0.0F, static_cast<float>(width), static_cast<float>(height), 0.0F, 1.0F };
336 cmd.setViewport(0, 1, &viewport);
337
338 vk::Rect2D scissor { { 0, 0 }, { width, height } };
339 cmd.setScissor(0, 1, &scissor);
340 }
341
342 flow.bind_pipeline(cmd_id, m_pipeline_id);
343
344 auto& descriptor_bindings = buffer->get_pipeline_context().descriptor_buffer_bindings;
345 if (!descriptor_bindings.empty()) {
346 for (const auto& binding : descriptor_bindings) {
347 if (binding.set >= m_descriptor_set_ids.size()) {
349 "Descriptor set index {} out of range", binding.set);
350 continue;
351 }
352
353 foundry.update_descriptor_buffer(
354 m_descriptor_set_ids[binding.set],
355 binding.binding,
356 binding.type,
357 binding.buffer_info.buffer,
358 binding.buffer_info.offset,
359 binding.buffer_info.range);
360 }
361 }
362
363 if (!m_descriptor_set_ids.empty()) {
364 flow.bind_descriptor_sets(cmd_id, m_pipeline_id, m_descriptor_set_ids);
365 }
366
367 const auto& staging = buffer->get_pipeline_context();
368 if (!staging.push_constant_staging.empty()) {
369 flow.push_constants(
370 cmd_id,
372 staging.push_constant_staging.data(),
373 staging.push_constant_staging.size());
374 } else if (!m_push_constant_data.empty()) {
375 flow.push_constants(
376 cmd_id,
379 m_push_constant_data.size());
380 }
381
382 on_before_execute(cmd_id, buffer);
383
384 flow.bind_vertex_buffers(cmd_id, { buffer });
385
386 flow.draw(cmd_id, vertex_layout->vertex_count);
387
388 foundry.end_commands(cmd_id);
389
390 buffer->set_pipeline_command(m_pipeline_id, cmd_id);
391 m_target_window->track_frame_command(cmd_id);
392
394 "Recorded secondary command buffer {} for window '{}'",
395 cmd_id, m_target_window->get_create_info().title);
396}
397
398void RenderProcessor::on_attach(const std::shared_ptr<Buffer>& buffer)
399{
401
402 auto vk_buffer = std::dynamic_pointer_cast<VKBuffer>(buffer);
403 if (vk_buffer && vk_buffer->has_vertex_layout()) {
404 auto vertex_layout = vk_buffer->get_vertex_layout();
405 if (vertex_layout.has_value()) {
407 "RenderProcessor: Auto-injecting vertex layout "
408 "({} vertices, {} attributes)",
409 vertex_layout->vertex_count,
410 vertex_layout->attributes.size());
411
413 m_buffer_info[vk_buffer] = { .semantic_layout = vertex_layout.value(), .use_reflection = false };
414 }
415 }
416
417 if (!m_display_service) {
420 }
421}
422
463
464} // namespace MayaFlux::Buffers
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_RT_WARN(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
#define MF_RT_TRACE(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
void bind_texture(uint32_t binding, const std::shared_ptr< Core::VKImage > &texture, vk::Sampler sampler=nullptr)
Bind a texture to a descriptor binding point.
Portal::Graphics::ShaderID m_fragment_shader_id
std::unordered_map< std::shared_ptr< VKBuffer >, VertexInfo > m_buffer_info
Registry::Service::DisplayService * m_display_service
Portal::Graphics::ShaderID m_geometry_shader_id
void set_tess_eval_shader(const std::string &tess_eval_path)
void set_target_window(std::shared_ptr< Core::Window > window)
bool on_before_execute(Portal::Graphics::CommandBufferID cmd_id, const std::shared_ptr< VKBuffer > &buffer) override
Called before each process callback.
void set_tess_control_shader(const std::string &tess_control_path)
Portal::Graphics::RenderPipelineID m_pipeline_id
Portal::Graphics::ShaderID m_tess_control_shader_id
void on_attach(const std::shared_ptr< Buffer > &buffer) override
Called when this processor is attached to a buffer.
void initialize_descriptors(const std::shared_ptr< VKBuffer > &buffer) override
void initialize_pipeline(const std::shared_ptr< VKBuffer > &buffer) override
std::unordered_map< uint32_t, TextureBinding > m_texture_bindings
void set_geometry_shader(const std::string &geometry_path)
Portal::Graphics::PrimitiveTopology m_primitive_topology
RenderProcessor(const ShaderConfig &config)
void set_fragment_shader(const std::string &fragment_path)
Portal::Graphics::PolygonMode m_polygon_mode
void execute_shader(const std::shared_ptr< VKBuffer > &buffer) override
Portal::Graphics::CullMode m_cull_mode
std::shared_ptr< Core::Window > m_target_window
Portal::Graphics::ShaderID m_tess_eval_shader_id
Portal::Graphics::ShaderID m_shader_id
std::vector< uint8_t > m_push_constant_data
virtual void on_descriptors_created()
Called after descriptor sets are created.
virtual void update_descriptors(const std::shared_ptr< VKBuffer > &buffer)
virtual void on_pipeline_created(Portal::Graphics::ComputePipelineID pipeline_id)
Called after pipeline is created.
virtual void on_before_descriptors_create()
Called before descriptor sets are created.
void on_attach(const std::shared_ptr< Buffer > &buffer) override
Called when this processor is attached to a buffer.
std::vector< Portal::Graphics::DescriptorSetID > m_descriptor_set_ids
Abstract base class for shader-based buffer processing.
void register_window_for_rendering(const std::shared_ptr< Core::Window > &window)
Register a window for dynamic rendering.
ShaderID load_shader(const std::string &content, std::optional< ShaderStage > stage=std::nullopt, const std::string &entry_point="main")
Universal shader loader - auto-detects source type.
Interface * get_service()
Query for a backend service.
static BackendRegistry & instance()
Get the global registry instance.
@ BufferProcessing
Buffer processing (Buffers::BufferManager, processing chains)
@ Buffers
Buffers, Managers, processors and processing chains.
constexpr RenderPipelineID INVALID_RENDER_PIPELINE
MAYAFLUX_API TextureLoom & get_texture_manager()
Get the global texture manager instance.
constexpr ShaderID INVALID_SHADER
MAYAFLUX_API RenderFlow & get_render_flow()
Get the global render flow instance.
MAYAFLUX_API ShaderFoundry & get_shader_foundry()
Get the global shader compiler instance.
std::string shader_path
Path to shader file.
std::unordered_map< std::string, ShaderBinding > bindings
std::vector< std::vector< DescriptorBindingInfo > > descriptor_sets
std::optional< Kakshya::VertexLayout > semantic_vertex_layout
std::vector< BlendAttachmentConfig > blend_attachments
Complete render pipeline configuration.
std::function< int(const std::shared_ptr< void > &)> get_swapchain_format
Get actual swapchain format for a window.
std::function< void(const std::shared_ptr< void > &, uint32_t &, uint32_t &)> get_swapchain_extent
Get swapchain extent for a window.
Backend display and presentation service interface.