53 std::vector<Kakshya::DataVariant>,
54 std::vector<Kakshya::DataVariant>> {
57 std::vector<Kakshya::DataVariant>,
58 std::vector<Kakshya::DataVariant>>;
84 uint32_t image_binding = 1,
85 std::vector<GpuBufferBinding> aux_bindings = {},
87 uint32_t output_binding = 0)
88 : Base(
std::move(config))
89 , m_output_format(output_format)
91 , m_aux_bindings(
std::move(aux_bindings))
93 m_image_slots.push_back({ .binding = { .set = 0,
94 .binding = image_binding,
95 .direction = GpuBufferBinding::Direction::INPUT,
96 .element_type = image_access } });
97 if (m_output_mode != OutputMode::SCALAR) {
98 m_image_slots.push_back({ .binding = { .set = 0,
99 .binding = output_binding,
100 .direction = GpuBufferBinding::Direction::OUTPUT,
101 .element_type = GpuBufferBinding::ElementType::IMAGE_STORAGE } });
121 const std::vector<GpuBufferBinding>& bindings,
125 , m_output_mode(mode)
127 for (
const auto&
b : bindings) {
128 const bool is_image =
b.element_type == GpuBufferBinding::ElementType::IMAGE_STORAGE
129 ||
b.element_type == GpuBufferBinding::ElementType::IMAGE_SAMPLED;
131 m_image_slots.push_back({ .binding =
b });
133 m_aux_bindings.push_back(
b);
167 m_output_dim_override = { w,
h };
172 m_output_dim_override = std::nullopt;
185 for (
auto& s : m_image_slots) {
186 if (s.binding.binding == binding_index)
189 error<std::runtime_error>(Journal::Component::Yantra, Journal::Context::BufferProcessing,
190 std::source_location::current(),
191 "TextureExecutionContext: no image slot at binding {}", binding_index);
211 if (!ensure_gpu_ready())
212 return Portal::Graphics::INVALID_FENCE;
214 auto [channels, structure_info] = extract_inputs(
input);
215 return dispatch_core_async(channels, structure_info);
228 result.
primary = readback_primary(last_effective_element_count());
229 readback_aux(result);
247 return collect_gpu_outputs(raw, {}, {});
264 if (ctx.
mode != ExecutionMode::CHAINED && ctx.
mode != ExecutionMode::CHAINED_INDIRECT)
265 return Base::execute(
input, ctx);
267 if (!ensure_gpu_ready()) {
268 error<std::runtime_error>(
269 Journal::Component::Yantra,
270 Journal::Context::BufferProcessing,
271 std::source_location::current(),
272 "TextureExecutionContext: GPU initialisation failed");
275 auto [ch_copies, structure_info] = extract_inputs(
input);
277 if (ctx.
mode == ExecutionMode::CHAINED_INDIRECT) {
278 dispatch_core_chained_indirect(ch_copies, structure_info, ctx);
280 dispatch_core_chained(ch_copies, structure_info, ctx);
303 const std::shared_ptr<Core::VKImage>&
image,
304 vk::Sampler sampler =
nullptr)
306 auto& slot = input_slot();
307 if (slot.binding.element_type == GpuBufferBinding::ElementType::IMAGE_STORAGE) {
308 stage_image_at(slot.binding.binding,
image, GpuBufferBinding::ElementType::IMAGE_STORAGE);
312 : Portal::Graphics::SamplerForge::instance().get_default_linear();
313 stage_image_at(slot.binding.binding,
image, GpuBufferBinding::ElementType::IMAGE_SAMPLED, s);
316 m_pending_container =
nullptr;
334 vk::Sampler sampler =
nullptr)
336 auto& slot = input_slot();
337 auto img = container.
to_image(layer);
340 : Portal::Graphics::SamplerForge::instance().get_default_linear();
341 stage_image_at(slot.binding.binding, img, GpuBufferBinding::ElementType::IMAGE_SAMPLED, s);
343 m_pending_container =
nullptr;
357 auto& slot = output_slot();
358 if (!slot.image || slot.width !=
width || slot.height !=
height) {
359 slot.image = Portal::Graphics::TextureLoom::instance()
360 .create_storage_image(
width,
height, m_output_format);
364 stage_image_at(slot.binding.binding, slot.image, GpuBufferBinding::ElementType::IMAGE_STORAGE);
378 std::vector<GpuBufferBinding> bindings;
379 for (
const auto& slot : m_image_slots)
380 bindings.push_back(slot.binding);
381 bindings.insert(bindings.end(), m_aux_bindings.begin(), m_aux_bindings.end());
393 m_pending_container = resolve_texture_container(
input);
403 const std::vector<std::vector<double>>& ,
406 if (m_pending_container) {
407 const auto sampler = Portal::Graphics::SamplerForge::instance().get_default_linear();
408 std::shared_ptr<Core::VKImage> img;
409 if (m_pending_container->get_layer_count() > 1) {
410 if (!m_upload_staging) {
411 m_upload_staging = Buffers::create_image_staging_buffer(
412 m_pending_container->byte_size() * m_pending_container->get_layer_count());
414 img = m_pending_container->to_image_array(m_upload_staging);
416 if (!m_upload_staging) {
417 m_upload_staging = Buffers::create_image_staging_buffer(
418 m_pending_container->byte_size());
420 img = m_pending_container->to_image(m_pending_layer, m_upload_staging);
423 auto& in_slot = input_slot();
424 if (in_slot.binding.element_type == GpuBufferBinding::ElementType::IMAGE_STORAGE) {
425 stage_image_at(in_slot.binding.binding, img, GpuBufferBinding::ElementType::IMAGE_STORAGE);
427 stage_image_at(in_slot.binding.binding, img, GpuBufferBinding::ElementType::IMAGE_SAMPLED, sampler);
431 if (m_output_mode == OutputMode::CONTAINER
432 || m_output_mode == OutputMode::IMAGE) {
433 const uint32_t ow = m_output_dim_override
434 ? m_output_dim_override->first
435 : m_pending_container->get_width();
436 const uint32_t oh = m_output_dim_override
437 ? m_output_dim_override->second
438 : m_pending_container->get_height();
439 prepare_output_image(ow, oh);
449 size_t total_elements,
452 const auto& ws = gpu_config().workgroup_size;
453 if (m_output_dim_override) {
454 const uint32_t w = m_output_dim_override->first;
455 const uint32_t
h = m_output_dim_override->second;
457 (w + ws[0] - 1) / ws[0],
458 (
h + ws[1] - 1) / ws[1],
462 if (m_pending_container) {
463 const uint32_t w = m_pending_container->get_width();
464 const uint32_t
h = m_pending_container->get_height();
466 (w + ws[0] - 1) / ws[0],
467 (
h + ws[1] - 1) / ws[1],
471 return Base::calculate_dispatch_size(total_elements, structure_info);
481 const std::vector<std::vector<double>>& ,
484 if (m_output_mode == OutputMode::SCALAR)
487 auto img = get_output_image(output_slot().binding.binding);
489 error<std::runtime_error>(
490 Journal::Component::Yantra,
491 Journal::Context::BufferProcessing,
492 std::source_location::current(),
493 "TextureExecutionContext: no output image at declared output binding after dispatch");
496 Portal::Graphics::TextureLoom::instance().transition_layout(
497 img, vk::ImageLayout::eGeneral, vk::ImageLayout::eShaderReadOnlyOptimal);
499 if (m_output_mode == OutputMode::IMAGE)
502 const uint32_t w = m_output_dim_override
503 ? m_output_dim_override->first
504 : (m_pending_container ? m_pending_container->get_width() : img->get_width());
505 const uint32_t
h = m_output_dim_override
506 ? m_output_dim_override->second
507 : (m_pending_container ? m_pending_container->get_height() : img->get_height());
509 if (!m_output_container) {
510 m_output_container = std::make_shared<Kakshya::TextureContainer>(
511 w,
h, m_output_format);
514 if (!m_download_staging) {
515 m_download_staging = Buffers::create_image_staging_buffer(
516 m_output_container->byte_size());
519 m_output_container->from_image(img, m_download_staging, 0);
522 result.
container = std::static_pointer_cast<Kakshya::SignalSourceContainer>(
540 std::shared_ptr<Core::VKImage>
image;
547 uint32_t m_pending_layer {};
568 for (
auto& s : m_image_slots) {
569 if (s.binding.direction == GpuBufferBinding::Direction::INPUT)
573 error<std::runtime_error>(
574 Journal::Component::Yantra,
575 Journal::Context::BufferProcessing,
576 std::source_location::current(),
577 "TextureExecutionContext: no input image slot declared");
587 for (
auto& s : m_image_slots) {
588 if (s.binding.direction == GpuBufferBinding::Direction::OUTPUT)
592 error<std::runtime_error>(
593 Journal::Component::Yantra,
594 Journal::Context::BufferProcessing,
595 std::source_location::current(),
596 "TextureExecutionContext: no output image slot declared (SCALAR mode?)");
609 return std::dynamic_pointer_cast<Kakshya::TextureContainer>(
input.container.value());
Core::GlobalInputConfig input
std::shared_ptr< Core::VKImage > to_image(uint32_t layer=0) const
Upload the pixel buffer to a new VKImage via TextureLoom.
SignalSourceContainer wrapping GPU texture data as addressable pixel bytes.
Type-parameterised shell over GpuDispatchCore.
std::shared_ptr< Kakshya::TextureContainer > m_output_container
ImageSlot & output_slot()
Find the declared output image slot.
std::shared_ptr< Buffers::VKBuffer > m_download_staging
output_type execute(const input_type &input, const ExecutionContext &ctx) override
Override for CHAINED mode: skips collect_gpu_outputs().
TextureExecutionContext(GpuComputeConfig config, Portal::Graphics::ImageFormat output_format=Portal::Graphics::ImageFormat::RGBA8, OutputMode mode=OutputMode::CONTAINER, uint32_t image_binding=1, std::vector< GpuBufferBinding > aux_bindings={}, GpuBufferBinding::ElementType image_access=GpuBufferBinding::ElementType::IMAGE_SAMPLED, uint32_t output_binding=0)
output_type collect_container_result()
Collect the output TextureContainer after a signaled async dispatch.
std::shared_ptr< Buffers::VKBuffer > m_upload_staging
void on_before_gpu_dispatch(const std::vector< std::vector< double > > &, const DataStructureInfo &) override
Stages the pending TextureContainer at the declared input image binding and, in CONTAINER or IMAGE mo...
void set_output_dimensions(uint32_t w, uint32_t h)
Override the output storage image dimensions for the next dispatch.
void set_input_layer(uint32_t layer)
Set the array layer staged from the input TextureContainer on the next dispatch_async call.
std::shared_ptr< Kakshya::TextureContainer > m_pending_container
output_type collect_gpu_outputs(const GpuChannelResult &, const std::vector< std::vector< double > > &, const DataStructureInfo &) override
In CONTAINER mode: downloads the storage image at the declared output binding into a new TextureConta...
static std::shared_ptr< Kakshya::TextureContainer > resolve_texture_container(const input_type &input)
Extracts a TextureContainer pointer from datum.container if present.
void prepare_output_image(uint32_t width, uint32_t height)
Allocates the output storage image and stages it at the declared output binding.
std::vector< ImageSlot > m_image_slots
Portal::Graphics::FenceID dispatch_async(const input_type &input)
Non-blocking dispatch.
ImageSlot & input_slot()
Find the declared input image slot.
std::vector< GpuBufferBinding > m_aux_bindings
void stage_container(const Kakshya::TextureContainer &container, uint32_t layer=0, vk::Sampler sampler=nullptr)
Stage a TextureContainer layer at the declared input image binding.
std::optional< std::pair< uint32_t, uint32_t > > m_output_dim_override
GpuChannelResult collect_result()
Collect SSBO readback after a signaled async dispatch.
void clear_output_dimensions()
std::array< uint32_t, 3 > calculate_dispatch_size(size_t total_elements, const DataStructureInfo &structure_info) const override
Derives dispatch size from the staged container dimensions when available; falls back to element-coun...
Portal::Graphics::ImageFormat m_output_format
void stage_image(const std::shared_ptr< Core::VKImage > &image, vk::Sampler sampler=nullptr)
Stage an arbitrary VKImage at the declared input image binding.
std::pair< std::vector< std::vector< double > >, DataStructureInfo > extract_inputs(const input_type &input) override
Stashes the TextureContainer from datum.container for use in on_before_gpu_dispatch; returns empty ch...
GpuBufferBinding & slot_binding(uint32_t binding_index)
Direct access to an image slot's binding descriptor by index.
TextureExecutionContext(GpuComputeConfig config, const std::vector< GpuBufferBinding > &bindings, OutputMode mode=OutputMode::SCALAR)
Construct from an explicit, fully-specified binding list.
std::vector< GpuBufferBinding > declare_buffer_bindings() const override
Declare the image bindings from m_image_slots, plus any aux SSBO bindings provided at construction.
GpuExecutionContext specialisation for image compute shaders.
ImageFormat
User-friendly image format enum.
@ RGBA8
Four channel 8-bit.
bool is_image(const fs::path &filepath)
ElementType
Element type the shader expects in this binding.
uint32_t binding
Binding index within the set.
Declares a single storage buffer or image binding a compute shader expects.
Plain-data description of the compute shader to dispatch.
Metadata about data structure for reconstruction.
std::optional< std::shared_ptr< Kakshya::SignalSourceContainer > > container
Optional reference to container, required for regions.
Input/Output container for computation pipeline data flow with structure preservation.
ExecutionMode mode
Execution mode controlling scheduling behavior.
Context information controlling how a compute operation executes.
std::vector< float > primary
Erased output of a GPU dispatch: reconstructed float data plus any raw auxiliary outputs keyed by bin...
std::shared_ptr< Core::VKImage > image
One declared image binding: its GpuBufferBinding descriptor, the currently staged VKImage,...