Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions PyroRHI/Api/IDevice.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -466,15 +466,12 @@ namespace PyroshockStudios {
PYRO_NODISCARD virtual DeviceSize ImageSizeRequirements(Image image) const = 0;

/**
* @brief Returns the required row pitch (stride in bytes) for a specific subresource of the image.
* @brief Returns the ImageUploadSlice for a specific subresource of the image.
*
* The row pitch is the number of bytes the GPU expects between consecutive rows in a buffer
* when copying to or from this image subresource. It must be used when performing
* buffer-to-image or image-to-buffer copies to avoid out-of-bounds errors.
*
* Row width is the minimal width that needs to be queried **INCLUDING** the format size. For a buffer-image copy, this is the extent of your copy region.
* @param slice Subresource of the image to upload to
* @param region Optional box where the upload is targetted towards. By default, the entire extent of the image is assumed.
*/
PYRO_NODISCARD virtual u32 ImageSubresourceRowPitch(Image image, u32 rowWidth, ImageSlice slice = {}) const = 0;
PYRO_NODISCARD virtual ImageUploadSlice ImageUploadRequirements(Image image, ImageSlice slice, eastl::optional<Box3D> region = eastl::nullopt) const = 0;

/**
* @brief - REQUIRES ACCELERATION STRUCTURE SUPPORT -
Expand Down
12 changes: 12 additions & 0 deletions PyroRHI/Api/Types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,18 @@ namespace PyroshockStudios {
#define PYRO_IMAGE_SLICE_RESOLVE_LEVELS(subSlice, fullLevelCount) \
((subSlice.levelCount == PYRO_REMAINING_MIP_LEVELS) ? (fullLevelCount - subSlice.baseMipLevel) : subSlice.levelCount)

struct ImageUploadSlice {
DeviceSize size = {};
u32 uploadPitch = {};
u32 blockSize = {};
u32 elementWidth = {};
u32 elementHeight = {};
u32 depth = {};
u32 uploadOffsetAlignment = {};

PYRO_NODISCARD eastl::string ToString(usize indentation = 0) const;
};

struct DrawArgumentBuffer {
u32 vertexCount = {};
u32 instanceCount = {};
Expand Down
5 changes: 5 additions & 0 deletions PyroRHI/ToString.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,11 @@ namespace PyroshockStudios {
inline eastl::string ImageSlice::ToString(usize indentation) const {
return eastl::string().sprintf("{ mip=%u, layer=%u }", mipLevel, arrayLayer);
}
inline eastl::string ImageUploadSlice::ToString(usize indentation) const {
return eastl::string().sprintf("{ size=%ull, uploadPitch=%u, blockSize=%u, elementWidth=%u, elementHeight=%u, depth=%u, uploadOffsetAlignment=%u }",
size, uploadPitch, blockSize, elementWidth, elementHeight, depth, uploadOffsetAlignment);
}



inline eastl::string Access::ToString(usize indentation) const {
Expand Down
31 changes: 22 additions & 9 deletions RHIDX12/Api/CommandBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,25 +54,38 @@ namespace PyroshockStudios {
const auto& dst = mDevice->ResourcePool().Get(info.image);

auto blockInfo = RHIUtil::GetFormatBlockInfo(dst.info.format);
u32 bufferRowPitch = (info.rowPitch / blockInfo.bytesPerBlock * blockInfo.blockWidth);

// calculate the padded height (in blocks) to know the true byte size of one slice
u32 elementHeight = (info.imageExtent.height + blockInfo.blockHeight - 1) / blockInfo.blockHeight;

// calculate exactly how many BYTES a single array slice takes up in the staging buffer
u64 layerByteSize = static_cast<u64>(info.rowPitch) * elementHeight * info.imageExtent.depth;

for (UINT j = 0; j < PYRO_IMAGE_SLICE_RESOLVE_LAYERS(info.imageSlice, dst.info.arrayLayerCount); ++j) {
UINT dstSubresource = D3D12CalcSubresource(info.imageSlice.mipLevel, info.imageSlice.baseArrayLayer + j, 0, dst.info.mipLevelCount, dst.info.arrayLayerCount);

D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint = {};
UINT numRows = {};
UINT64 rowSizesInBytes = {};
UINT64 requiredSize = {};
mDevice->InternalDevice()->GetCopyableFootprints(&dst.desc, dstSubresource, 1, info.bufferOffset,
&footprint, &numRows, &rowSizesInBytes, &requiredSize);
ASSERT(PYRO_VERIFY_ALIGNMENT(info.rowPitch, footprint.Footprint.RowPitch), "Row Pitch MUST be aligned to device requirements!");
footprint.Footprint.RowPitch = info.rowPitch;
// Use the DXGI format from the destination texture desc
footprint.Footprint.Format = dst.desc.Format;

// Exact pixel dimensions requested
footprint.Footprint.Width = info.imageExtent.width;
footprint.Footprint.Height = info.imageExtent.height;
footprint.Footprint.Depth = info.imageExtent.depth;
footprint.Offset = bufferRowPitch * footprint.Footprint.Height * footprint.Footprint.Depth * j;

footprint.Footprint.RowPitch = info.rowPitch;

// start at the requested buffer offset, and advance by the exact BYTE size of each layer
footprint.Offset = info.bufferOffset + (layerByteSize * j);

ASSERT(PYRO_VERIFY_ALIGNMENT(footprint.Offset, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT), "Buffer Offset MUST be 512-byte aligned!");

CD3DX12_TEXTURE_COPY_LOCATION Dst(dst.resource.Get(), dstSubresource);
CD3DX12_TEXTURE_COPY_LOCATION Src(src.resource.Get(), footprint);

mCommandList->CopyTextureRegion(&Dst, info.imageOffset.x, info.imageOffset.y, info.imageOffset.z, &Src, nullptr);
}

gDx12Context->FlushDebugMessages();
}

Expand Down
36 changes: 34 additions & 2 deletions RHIDX12/Api/Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ namespace PyroshockStudios {
gDx12Context->FlushDebugMessages();
return resourceAllocInfo.SizeInBytes;
}
u32 D3DDevice::ImageSubresourceRowPitch(Image image, u32 rowWidth, ImageSlice slice) const {
ImageUploadSlice D3DDevice::ImageUploadRequirements(Image image, ImageSlice slice, eastl::optional<Box3D> region) const {
auto& img = mResourcePool->Get(image);
UINT dstSubresource = D3D12CalcSubresource(slice.mipLevel, slice.arrayLayer, 0, img.info.mipLevelCount, img.info.arrayLayerCount);
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint = {};
Expand All @@ -175,7 +175,39 @@ namespace PyroshockStudios {
mDevice->GetCopyableFootprints(&img.desc, dstSubresource, 1, 0,
&footprint, &numRows, &rowSizesInBytes, &requiredSize);
gDx12Context->FlushDebugMessages();
return footprint.Footprint.RowPitch;


auto blockInfo = RHIUtil::GetFormatBlockInfo(img.info.format);

Extent3D mipExtent = img.info.size;
if (slice.mipLevel > 0) {
u32 divisions = 1 << slice.mipLevel;
mipExtent.width /= divisions;
mipExtent.height /= divisions;
mipExtent.depth /= divisions;
if (mipExtent.width == 0)
mipExtent.width = 1;
if (mipExtent.height == 0)
mipExtent.height = 1;
if (mipExtent.depth == 0)
mipExtent.depth = 1;
}

Box3D box = region.value_or(Box3D::Cut(mipExtent));
u32 elementWidth = (box.width + blockInfo.blockWidth - 1) / blockInfo.blockWidth;
u32 elementHeight = (box.height + blockInfo.blockHeight - 1) / blockInfo.blockHeight;

ImageUploadSlice uploadSlice;
uploadSlice.uploadPitch = footprint.Footprint.RowPitch;
uploadSlice.blockSize = blockInfo.bytesPerBlock;
uploadSlice.elementWidth = elementWidth;
uploadSlice.elementHeight = elementHeight;
uploadSlice.depth = box.depth;
uploadSlice.uploadOffsetAlignment = D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT;

uploadSlice.size = static_cast<DeviceSize>(uploadSlice.uploadPitch) * elementHeight * box.depth;

return uploadSlice;
}
AccelerationStructureBuildSizesInfo D3DDevice::BlasSizeRequirements(const BlasBuildInfo& info) const {
if (!mDevice5) {
Expand Down
2 changes: 1 addition & 1 deletion RHIDX12/Api/Device.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ namespace PyroshockStudios {
u8* BufferHostAddress(Buffer buffer) const override;
BlasAddress BlasInstanceAddress(BlasId blas) const override;
DeviceSize ImageSizeRequirements(Image image) const override;
u32 ImageSubresourceRowPitch(Image image, u32 rowWidth, ImageSlice slice) const override;
ImageUploadSlice ImageUploadRequirements(Image image, ImageSlice slice, eastl::optional<Box3D> region) const override;

AccelerationStructureBuildSizesInfo BlasSizeRequirements(const BlasBuildInfo& info) const override;
AccelerationStructureBuildSizesInfo TlasSizeRequirements(const TlasBuildInfo& info) const override;
Expand Down
8 changes: 5 additions & 3 deletions RHIVulkan/Api/CommandBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,14 @@ namespace PyroshockStudios::RHIVulkan {

ImplImageSlot& imageSlot = mDevice->Slot(info.image);
CheckIfSwapchainReference(imageSlot);
auto blockInfo = RHIUtil::GetFormatBlockInfo(imageSlot.info.format);

u32 alignedBufferHeight = (info.imageExtent.height + blockInfo.blockHeight - 1) / blockInfo.blockHeight * blockInfo.blockHeight;

auto blockInfo = RHIUtil::GetFormatBlockInfo(imageSlot.info.format);
const VkBufferImageCopy region = {
.bufferOffset = info.bufferOffset,
.bufferRowLength = info.rowPitch == 0 ? 0 : (info.rowPitch / blockInfo.bytesPerBlock * blockInfo.blockWidth ),
.bufferImageHeight = info.imageExtent.height,
.bufferRowLength = info.rowPitch == 0 ? 0 : (info.rowPitch / blockInfo.bytesPerBlock * blockInfo.blockWidth),
.bufferImageHeight = alignedBufferHeight,
.imageSubresource = {
.aspectMask = imageSlot.aspectFlags,
.mipLevel = info.imageSlice.mipLevel,
Expand All @@ -145,6 +146,7 @@ namespace PyroshockStudios::RHIVulkan {
.imageOffset = { (int32_t)info.imageOffset.x, (int32_t)info.imageOffset.y, (int32_t)info.imageOffset.z },
.imageExtent = { info.imageExtent.width, info.imageExtent.height, info.imageExtent.depth }
};

vkCmdCopyBufferToImage(mCommandBuffer, mDevice->Slot(info.buffer).vkBuffer,
imageSlot.vkImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
}
Expand Down
35 changes: 31 additions & 4 deletions RHIVulkan/Api/Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1309,17 +1309,44 @@ namespace PyroshockStudios {
return static_cast<BlasAddress>(Slot(blas).deviceAddress);
}


DeviceSize VulkanDevice::ImageSizeRequirements(Image image) const {
auto& img = Slot(image);
VkMemoryRequirements requirements;
vkGetImageMemoryRequirements(mDevice, img.vkImage, &requirements);
return requirements.size;
}
u32 VulkanDevice::ImageSubresourceRowPitch(Image image, u32 rowWidth, ImageSlice slice) const {
return PYRO_ALIGN(rowWidth, mPhysicalDeviceProperties.limits.optimalBufferCopyRowPitchAlignment);
}

ImageUploadSlice VulkanDevice::ImageUploadRequirements(Image image, ImageSlice slice, eastl::optional<Box3D> region) const {
auto& img = Slot(image);
auto blockInfo = RHIUtil::GetFormatBlockInfo(img.info.format);

Extent3D mipExtent = img.info.size;
if (slice.mipLevel > 0) {
u32 divisions = 1 << slice.mipLevel;
mipExtent.width = std::max(1u, mipExtent.width / divisions);
mipExtent.height = std::max(1u, mipExtent.height / divisions);
mipExtent.depth = std::max(1u, mipExtent.depth / divisions);
}

Box3D box = region.value_or(Box3D::Cut(mipExtent));

u32 elementWidth = (box.width + blockInfo.blockWidth - 1) / blockInfo.blockWidth;
u32 elementHeight = (box.height + blockInfo.blockHeight - 1) / blockInfo.blockHeight;

u32 rowWidthBytes = elementWidth * blockInfo.bytesPerBlock;

ImageUploadSlice uploadSlice;
uploadSlice.uploadPitch = PYRO_ALIGN(rowWidthBytes, mPhysicalDeviceProperties.limits.optimalBufferCopyRowPitchAlignment);
uploadSlice.blockSize = blockInfo.bytesPerBlock;
uploadSlice.elementWidth = elementWidth;
uploadSlice.elementHeight = elementHeight;
uploadSlice.depth = box.depth;
uploadSlice.uploadOffsetAlignment = mPhysicalDeviceProperties.limits.optimalBufferCopyOffsetAlignment;

uploadSlice.size = static_cast<DeviceSize>(uploadSlice.uploadPitch) * uploadSlice.elementHeight * uploadSlice.depth;

return uploadSlice;
}

void VulkanDevice::CreateAccelerationStructureBuildInfo(
const eastl::span<const TlasBuildInfo>& tlasBuildInfos, const eastl::span<const BlasBuildInfo>& blasBuildInfos,
Expand Down
2 changes: 1 addition & 1 deletion RHIVulkan/Api/Device.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ namespace PyroshockStudios {
BlasAddress BlasInstanceAddress(BlasId blas) const override;

DeviceSize ImageSizeRequirements(Image image) const override;
u32 ImageSubresourceRowPitch(Image image, u32 rowWidth, ImageSlice slice) const override;
ImageUploadSlice ImageUploadRequirements(Image image, ImageSlice slice, eastl::optional<Box3D> region) const override;
AccelerationStructureBuildSizesInfo BlasSizeRequirements(const BlasBuildInfo& info) const override;
AccelerationStructureBuildSizesInfo TlasSizeRequirements(const TlasBuildInfo& info) const override;

Expand Down
Loading
Loading