Skip to content

Commit

Permalink
[renderer] implement map[buffer|image]
Browse files Browse the repository at this point in the history
implement a method for transfer encoder to map memory directly.

this allows us to forego unnecessary extra allocations- especially when
decoding images:

1. map memory for image upload
2. read pixels into directly this memory

you must not access the mapped memory regions outside the current
transfer pass - but that's fine - renderpasses should be read-only when
it comes to the outside world, and they certainly are not allowed to
persistently store any temporary objects that they have acquired.
  • Loading branch information
tgfrerer committed Jan 30, 2024
1 parent 14fc091 commit d7b6d3a
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 1 deletion.
80 changes: 80 additions & 0 deletions modules/le_renderer/le_command_buffer_encoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,38 @@ static void cbe_write_to_buffer( le_command_buffer_encoder_o* self, le_buf_resou

// ----------------------------------------------------------------------

static void cbe_map_buffer( le_command_buffer_encoder_o* self, le_buf_resource_handle const& dst_buffer, size_t dst_offset, size_t numBytes, void** p_mem_addr ) {

auto cmd = self->mCommandStream->emplace_cmd<le::CommandWriteToBuffer>();

using namespace le_backend_vk; // for le_allocator_linear_i
le_buf_resource_handle srcResourceId;

// -- Allocate memory using staging allocator
//
// We don't use the encoder local scratch linear allocator, since memory written to buffers is
// typically a lot larger than uniforms and other small settings structs. Staging memory is
// allocated so that it is only used for TRANSFER_SRC, and shared amongst encoders so that we
// use available memory more efficiently.
//
if ( le_staging_allocator_i.map( self->stagingAllocator, numBytes, p_mem_addr, &srcResourceId ) ) {

cmd->info.src_buffer_id = srcResourceId;
cmd->info.src_offset = 0; // staging allocator will give us a fresh buffer, and src memory will be placed at its start
cmd->info.dst_offset = dst_offset;
cmd->info.numBytes = numBytes;
cmd->info.dst_buffer_id = dst_buffer;

// -- Whoever mapped this memory may now write to it - it will be available for the full duration of this frame
} else {
std::cerr << "ERROR " << __PRETTY_FUNCTION__ << " could not map" << numBytes << " Bytes." << std::endl
<< std::flush;
return;
}
}

// ----------------------------------------------------------------------

static void cbe_write_to_image( le_command_buffer_encoder_o* self,
le_img_resource_handle const& dst_img,
le_write_to_image_settings_t const& writeInfo,
Expand Down Expand Up @@ -773,7 +805,53 @@ static void cbe_write_to_image( le_command_buffer_encoder_o* self,
return;
}
}
// ----------------------------------------------------------------------

static void cbe_map_image( le_command_buffer_encoder_o* self,
le_img_resource_handle const& dst_img,
le_write_to_image_settings_t const& writeInfo,
size_t numBytes,
void** p_mem_addr ) {

// ----------| invariant: resource info represents an image

auto cmd = self->mCommandStream->emplace_cmd<le::CommandWriteToImage>();

using namespace le_backend_vk; // for le_allocator_linear_i
void* memAddr;
le_buf_resource_handle stagingBufferId;

// -- Allocate memory using staging allocator
//
// We don't use the encoder local scratch linear allocator, since memory written to buffers is
// typically a lot larger than uniforms and other small settings structs. Staging memory is also
// allocated so that it is only used for TRANSFER_SRC, and shared amongst encoders so that we
// use available memory more efficiently.
//
if ( le_staging_allocator_i.map( self->stagingAllocator, numBytes, p_mem_addr, &stagingBufferId ) ) {

assert( writeInfo.num_miplevels != 0 ); // number of miplevels must be at least 1.

cmd->info.src_buffer_id = stagingBufferId; // resource id of staging buffer
cmd->info.numBytes = numBytes; // total number of bytes from staging buffer which need to be synchronised.
cmd->info.dst_image_id = dst_img; // resource id for target image resource
cmd->info.dst_miplevel = writeInfo.dst_miplevel; // default 0, use higher number to manually upload higher mip levels.
cmd->info.dst_array_layer = writeInfo.dst_array_layer; // default 0, use higher number to manually upload to array layer / or mipmap face.
cmd->info.num_miplevels = writeInfo.num_miplevels; // default is 1, *must not* be 0. More than 1 means to auto-generate these miplevels
cmd->info.image_w = writeInfo.image_w; // image extent
cmd->info.image_h = writeInfo.image_h; // image extent
cmd->info.image_d = writeInfo.image_d; // image depth
cmd->info.offset_x = writeInfo.offset_x; // x offset into image where to place data
cmd->info.offset_y = writeInfo.offset_y; // y offset into image where to place data
cmd->info.offset_z = writeInfo.offset_z; // z offset into target image

// -- You may now write data to the freshly allocated buffer - it will stay mapped for the will
} else {
std::cerr << "ERROR " << __PRETTY_FUNCTION__ << " could not allocate " << numBytes << " Bytes." << std::endl
<< std::flush;
return;
}
}
// ----------------------------------------------------------------------
static void cbe_set_push_constant_data( le_command_buffer_encoder_o* self, void const* src_data, uint64_t num_bytes ) {

Expand Down Expand Up @@ -988,6 +1066,8 @@ void register_le_command_buffer_encoder_api( void* api_ ) {
.write_to_buffer = cbe_write_to_buffer,
.write_to_image = cbe_write_to_image,
.buffer_memory_barrier = cbe_buffer_memory_barrier,
.map_image_memory = cbe_map_image,
.map_buffer_memory = cbe_map_buffer,
};

cbe_rtx_i = {
Expand Down
5 changes: 4 additions & 1 deletion modules/le_renderer/le_renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,10 @@ struct le_renderer_api {
void ( *write_to_buffer )( le_command_buffer_encoder_o *self, le_buf_resource_handle const& dst_buffer, size_t dst_offset, void const* data, size_t numBytes);
void ( *write_to_image )( le_command_buffer_encoder_o *self, le_img_resource_handle const& dst_img, le_write_to_image_settings_t const & writeInfo, void const *data, size_t numBytes );
void ( *buffer_memory_barrier )( le_command_buffer_encoder_o *self, le::PipelineStageFlags2 const srcStageMask, le::PipelineStageFlags2 const dstStageMask, le::AccessFlags2 const dstAccessMask, le_buf_resource_handle const buffer, uint64_t const offset, uint64_t const range );
};

void ( *map_image_memory )( le_command_buffer_encoder_o *self, le_img_resource_handle const& dst_img, le_write_to_image_settings_t const & writeInfo, size_t numBytes, void** p_memory_addr );
void ( *map_buffer_memory )( le_command_buffer_encoder_o *self, le_buf_resource_handle const& dst_buffer, size_t dst_offset, size_t numBytes, void ** p_memory_addr);
};

struct command_buffer_video_decoder_encoder_interface_t{
void ( *execute_callback )( le_command_buffer_encoder_o *self, le::CommandVideoDecoderExecuteCallback::callback_fun_t* fun, void * user_data);
Expand Down
10 changes: 10 additions & 0 deletions modules/le_renderer/le_renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,16 @@ class TransferEncoder {
return *this;
}

TransferEncoder& mapBufferMemory( le_buf_resource_handle const& dstBuffer, size_t const& byteOffsetDst, size_t const& numBytes, void** p_mem_addr ) {
le_renderer::encoder_transfer_i.map_buffer_memory( self, dstBuffer, byteOffsetDst, numBytes, p_mem_addr );
return *this;
}

TransferEncoder& mapImageMemory( le_img_resource_handle const& dstImg, le_write_to_image_settings_t const& writeInfo, size_t const& numBytes, void** p_mem_addr ) {
le_renderer::encoder_transfer_i.map_image_memory( self, dstImg, writeInfo, numBytes, p_mem_addr );
return *this;
}

TransferEncoder& bufferMemoryBarrier(
le::PipelineStageFlags2 const& srcStageMask,
le::PipelineStageFlags2 const& dstStageMask,
Expand Down

0 comments on commit d7b6d3a

Please sign in to comment.