Subresource Mapping (#459)

* subresource download, upload improvements, ktx2, basis convert: using mipmap data from GPU

* bush and tree terrain assets: mipmap coverage fix

* refactors

* comments
This commit is contained in:
Turánszki János
2022-06-10 18:35:49 +02:00
committed by GitHub
parent 57468fe3b1
commit 7985d06b2c
8 changed files with 350 additions and 133 deletions
Binary file not shown.
Binary file not shown.
+13 -8
View File
@@ -443,9 +443,9 @@ namespace wi::graphics
TEXTURE_2D,
TEXTURE_3D,
} type = Type::TEXTURE_2D;
uint32_t width = 0;
uint32_t height = 0;
uint32_t depth = 0;
uint32_t width = 1;
uint32_t height = 1;
uint32_t depth = 1;
uint32_t array_size = 1;
uint32_t mip_levels = 1;
Format format = Format::UNKNOWN;
@@ -769,9 +769,9 @@ namespace wi::graphics
struct SubresourceData
{
const void* data_ptr = nullptr;
uint32_t row_pitch = 0;
uint32_t slice_pitch = 0;
const void* data_ptr = nullptr; // pointer to the beginning of the subresource data (pointer to beginning of resource + subresource offset)
uint32_t row_pitch = 0; // bytes between two rows of a texture (2D and 3D textures)
uint32_t slice_pitch = 0; // bytes between two depth slices of a texture (3D textures only)
};
struct Rect
@@ -816,8 +816,9 @@ namespace wi::graphics
constexpr bool IsBuffer() const { return type == Type::BUFFER; }
constexpr bool IsAccelerationStructure() const { return type == Type::RAYTRACING_ACCELERATION_STRUCTURE; }
void* mapped_data = nullptr;
uint32_t mapped_rowpitch = 0;
// These are only valid if the resource was created with CPU access (USAGE::UPLOAD or USAGE::READBACK)
void* mapped_data = nullptr; // for buffers, it is a pointer to the buffer data; for textures, it is a pointer to texture data with linear tiling;
size_t mapped_size = 0; // for buffers, it is the full buffer size; for textures it is the full texture size including all subresources;
};
struct GPUBuffer : public GPUResource
@@ -831,6 +832,10 @@ namespace wi::graphics
{
TextureDesc desc;
// These are only valid if the texture was created with CPU access (USAGE::UPLOAD or USAGE::READBACK)
const SubresourceData* mapped_subresources = nullptr; // an array of subresource mappings in the following memory layout: slice0|mip0, slice0|mip1, slice0|mip2, ... sliceN|mipN
size_t mapped_subresource_count = 0; // the array size of mapped_subresources (number of slices * number of miplevels)
constexpr const TextureDesc& GetDesc() const { return desc; }
};
+99 -47
View File
@@ -1308,10 +1308,14 @@ namespace dx12_internal
wi::vector<SingleDescriptor> subresources_uav;
SingleDescriptor uav_raw;
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint;
D3D12_GPU_VIRTUAL_ADDRESS gpu_address = 0;
UINT64 total_size = 0;
wi::vector<D3D12_PLACED_SUBRESOURCE_FOOTPRINT> footprints;
wi::vector<UINT64> rowSizesInBytes;
wi::vector<UINT> numRows;
wi::vector<SubresourceData> mapped_subresources;
virtual ~Resource_DX12()
{
allocationhandler->destroylocker.lock();
@@ -2997,7 +3001,7 @@ using namespace dx12_internal;
buffer->internal_state = internal_state;
buffer->type = GPUResource::Type::BUFFER;
buffer->mapped_data = nullptr;
buffer->mapped_rowpitch = 0;
buffer->mapped_size = 0;
buffer->desc = *desc;
HRESULT hr = E_FAIL;
@@ -3047,8 +3051,6 @@ using namespace dx12_internal;
resourceState = D3D12_RESOURCE_STATE_GENERIC_READ;
}
device->GetCopyableFootprints(&resourceDesc, 0, 1, 0, &internal_state->footprint, nullptr, nullptr, nullptr);
hr = allocationhandler->allocator->CreateResource(
&allocationDesc,
&resourceDesc,
@@ -3065,14 +3067,14 @@ using namespace dx12_internal;
{
hr = internal_state->resource->Map(0, nullptr, &buffer->mapped_data);
assert(SUCCEEDED(hr));
buffer->mapped_rowpitch = static_cast<uint32_t>(desc->size);
buffer->mapped_size = static_cast<uint32_t>(desc->size);
}
else if (desc->usage == Usage::UPLOAD)
{
D3D12_RANGE read_range = {};
hr = internal_state->resource->Map(0, &read_range, &buffer->mapped_data);
assert(SUCCEEDED(hr));
buffer->mapped_rowpitch = static_cast<uint32_t>(desc->size);
buffer->mapped_size = static_cast<uint32_t>(desc->size);
}
// Issue data copy on request:
@@ -3123,7 +3125,9 @@ using namespace dx12_internal;
texture->internal_state = internal_state;
texture->type = GPUResource::Type::TEXTURE;
texture->mapped_data = nullptr;
texture->mapped_rowpitch = 0;
texture->mapped_size = 0;
texture->mapped_subresources = nullptr;
texture->mapped_subresource_count = 0;
texture->desc = *desc;
HRESULT hr = E_FAIL;
@@ -3207,14 +3211,33 @@ using namespace dx12_internal;
resourceState = D3D12_RESOURCE_STATE_COMMON;
}
if (texture->desc.mip_levels == 0)
{
texture->desc.mip_levels = (uint32_t)log2(std::max(texture->desc.width, texture->desc.height)) + 1;
}
internal_state->total_size = 0;
internal_state->footprints.resize(desc->array_size * std::max(1u, desc->mip_levels));
internal_state->rowSizesInBytes.resize(internal_state->footprints.size());
internal_state->numRows.resize(internal_state->footprints.size());
device->GetCopyableFootprints(
&resourcedesc,
0,
(UINT)internal_state->footprints.size(),
0,
internal_state->footprints.data(),
internal_state->numRows.data(),
internal_state->rowSizesInBytes.data(),
&internal_state->total_size
);
if (texture->desc.usage == Usage::READBACK || texture->desc.usage == Usage::UPLOAD)
{
UINT64 RequiredSize = 0;
device->GetCopyableFootprints(&resourcedesc, 0, 1, 0, &internal_state->footprint, nullptr, nullptr, &RequiredSize);
resourcedesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
resourcedesc.Width = RequiredSize;
resourcedesc.Width = internal_state->total_size;
resourcedesc.Height = 1;
resourcedesc.DepthOrArraySize = 1;
resourcedesc.MipLevels = 1;
resourcedesc.Format = DXGI_FORMAT_UNKNOWN;
resourcedesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
resourcedesc.Flags = D3D12_RESOURCE_FLAG_NONE;
@@ -3245,54 +3268,54 @@ using namespace dx12_internal;
{
hr = internal_state->resource->Map(0, nullptr, &texture->mapped_data);
assert(SUCCEEDED(hr));
texture->mapped_rowpitch = internal_state->footprint.Footprint.RowPitch;
}
else if(texture->desc.usage == Usage::UPLOAD)
{
D3D12_RANGE read_range = {};
hr = internal_state->resource->Map(0, &read_range, &texture->mapped_data);
assert(SUCCEEDED(hr));
texture->mapped_rowpitch = internal_state->footprint.Footprint.RowPitch;
}
if (texture->desc.mip_levels == 0)
if (texture->mapped_data != nullptr)
{
texture->desc.mip_levels = (uint32_t)log2(std::max(texture->desc.width, texture->desc.height)) + 1;
texture->mapped_size = internal_state->total_size;
internal_state->mapped_subresources.resize(internal_state->footprints.size());
for (size_t i = 0; i < internal_state->footprints.size(); ++i)
{
internal_state->mapped_subresources[i].data_ptr = (uint8_t*)texture->mapped_data + internal_state->footprints[i].Offset;
internal_state->mapped_subresources[i].row_pitch = internal_state->footprints[i].Footprint.RowPitch;
internal_state->mapped_subresources[i].slice_pitch = internal_state->footprints[i].Footprint.RowPitch * internal_state->footprints[i].Footprint.Height;
}
texture->mapped_subresources = internal_state->mapped_subresources.data();
texture->mapped_subresource_count = internal_state->mapped_subresources.size();
}
// Issue data copy on request:
if (initial_data != nullptr)
{
uint32_t dataCount = desc->array_size * std::max(1u, desc->mip_levels);
wi::vector<D3D12_SUBRESOURCE_DATA> data(dataCount);
for (uint32_t slice = 0; slice < dataCount; ++slice)
wi::vector<D3D12_SUBRESOURCE_DATA> data(internal_state->footprints.size());
for (size_t i = 0; i < internal_state->footprints.size(); ++i)
{
data[slice] = _ConvertSubresourceData(initial_data[slice]);
data[i] = _ConvertSubresourceData(initial_data[i]);
}
UINT64 RequiredSize = 0;
wi::vector<D3D12_PLACED_SUBRESOURCE_FOOTPRINT> layouts(dataCount);
wi::vector<UINT64> rowSizesInBytes(dataCount);
wi::vector<UINT> numRows(dataCount);
device->GetCopyableFootprints(&resourcedesc, 0, dataCount, 0, layouts.data(), numRows.data(), rowSizesInBytes.data(), &RequiredSize);
auto cmd = copyAllocator.allocate(internal_state->total_size);
auto cmd = copyAllocator.allocate(RequiredSize);
for (uint32_t i = 0; i < dataCount; ++i)
for (size_t i = 0; i < internal_state->footprints.size(); ++i)
{
if (rowSizesInBytes[i] > (SIZE_T)-1)
if (internal_state->rowSizesInBytes[i] > (SIZE_T)-1)
continue;
D3D12_MEMCPY_DEST DestData = {};
DestData.pData = (void*)((UINT64)cmd.uploadbuffer.mapped_data + layouts[i].Offset);
DestData.RowPitch = (SIZE_T)layouts[i].Footprint.RowPitch;
DestData.SlicePitch = (SIZE_T)layouts[i].Footprint.RowPitch * (SIZE_T)numRows[i];
MemcpySubresource(&DestData, &data[i], (SIZE_T)rowSizesInBytes[i], numRows[i], layouts[i].Footprint.Depth);
DestData.pData = (void*)((UINT64)cmd.uploadbuffer.mapped_data + internal_state->footprints[i].Offset);
DestData.RowPitch = (SIZE_T)internal_state->footprints[i].Footprint.RowPitch;
DestData.SlicePitch = (SIZE_T)internal_state->footprints[i].Footprint.RowPitch * (SIZE_T)internal_state->numRows[i];
MemcpySubresource(&DestData, &data[i], (SIZE_T)internal_state->rowSizesInBytes[i], internal_state->numRows[i], internal_state->footprints[i].Footprint.Depth);
}
for (UINT i = 0; i < dataCount; ++i)
for (UINT i = 0; i < internal_state->footprints.size(); ++i)
{
CD3DX12_TEXTURE_COPY_LOCATION Dst(internal_state->resource.Get(), i);
CD3DX12_TEXTURE_COPY_LOCATION Src(to_internal(&cmd.uploadbuffer)->resource.Get(), layouts[i]);
CD3DX12_TEXTURE_COPY_LOCATION Src(to_internal(&cmd.uploadbuffer)->resource.Get(), internal_state->footprints[i]);
cmd.commandList->CopyTextureRegion(
&Dst,
0,
@@ -4992,13 +5015,26 @@ using namespace dx12_internal;
internal_state->allocationhandler = allocationhandler;
internal_state->resource = swapchain_internal->backBuffers[swapchain_internal->swapChain->GetCurrentBackBufferIndex()];
D3D12_RESOURCE_DESC desc = internal_state->resource->GetDesc();
device->GetCopyableFootprints(&desc, 0, 1, 0, &internal_state->footprint, nullptr, nullptr, nullptr);
D3D12_RESOURCE_DESC resourcedesc = internal_state->resource->GetDesc();
internal_state->total_size = 0;
internal_state->footprints.resize(resourcedesc.DepthOrArraySize * resourcedesc.MipLevels);
internal_state->rowSizesInBytes.resize(internal_state->footprints.size());
internal_state->numRows.resize(internal_state->footprints.size());
device->GetCopyableFootprints(
&resourcedesc,
0,
(UINT)internal_state->footprints.size(),
0,
internal_state->footprints.data(),
internal_state->numRows.data(),
internal_state->rowSizesInBytes.data(),
&internal_state->total_size
);
Texture result;
result.type = GPUResource::Type::TEXTURE;
result.internal_state = internal_state;
result.desc = _ConvertTextureDesc_Inv(desc);
result.desc = _ConvertTextureDesc_Inv(resourcedesc);
return result;
}
@@ -5512,19 +5548,35 @@ using namespace dx12_internal;
CommandList_DX12& commandlist = GetCommandList(cmd);
auto internal_state_src = to_internal(pSrc);
auto internal_state_dst = to_internal(pDst);
D3D12_RESOURCE_DESC desc_src = internal_state_src->resource->GetDesc();
D3D12_RESOURCE_DESC desc_dst = internal_state_dst->resource->GetDesc();
if (desc_dst.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER && desc_src.Dimension != D3D12_RESOURCE_DIMENSION_BUFFER)
const TextureDesc& src_desc = ((const Texture*)pSrc)->GetDesc();
const TextureDesc& dst_desc = ((const Texture*)pDst)->GetDesc();
if (src_desc.usage == Usage::UPLOAD)
{
CD3DX12_TEXTURE_COPY_LOCATION Src(internal_state_src->resource.Get(), 0);
CD3DX12_TEXTURE_COPY_LOCATION Dst(internal_state_dst->resource.Get(), internal_state_dst->footprint);
commandlist.GetGraphicsCommandList()->CopyTextureRegion(&Dst, 0, 0, 0, &Src, nullptr);
for (uint32_t layer = 0; layer < dst_desc.array_size; ++layer)
{
for (uint32_t mip = 0; mip < dst_desc.mip_levels; ++mip)
{
UINT subresource = D3D12CalcSubresource(mip, layer, 0, dst_desc.mip_levels, dst_desc.array_size);
CD3DX12_TEXTURE_COPY_LOCATION Src(internal_state_src->resource.Get(), internal_state_src->footprints[layer * dst_desc.mip_levels + mip]);
CD3DX12_TEXTURE_COPY_LOCATION Dst(internal_state_dst->resource.Get(), subresource);
commandlist.GetGraphicsCommandList()->CopyTextureRegion(&Dst, 0, 0, 0, &Src, nullptr);
}
}
}
else if (desc_src.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER && desc_dst.Dimension != D3D12_RESOURCE_DIMENSION_BUFFER)
else if (dst_desc.usage == Usage::READBACK)
{
CD3DX12_TEXTURE_COPY_LOCATION Src(internal_state_src->resource.Get(), internal_state_src->footprint);
CD3DX12_TEXTURE_COPY_LOCATION Dst(internal_state_dst->resource.Get(), 0);
commandlist.GetGraphicsCommandList()->CopyTextureRegion(&Dst, 0, 0, 0, &Src, nullptr);
for (uint32_t layer = 0; layer < dst_desc.array_size; ++layer)
{
for (uint32_t mip = 0; mip < dst_desc.mip_levels; ++mip)
{
UINT subresource = D3D12CalcSubresource(mip, layer, 0, dst_desc.mip_levels, dst_desc.array_size);
CD3DX12_TEXTURE_COPY_LOCATION Src(internal_state_src->resource.Get(), subresource);
CD3DX12_TEXTURE_COPY_LOCATION Dst(internal_state_dst->resource.Get(), internal_state_dst->footprints[layer * dst_desc.mip_levels + mip]);
commandlist.GetGraphicsCommandList()->CopyTextureRegion(&Dst, 0, 0, 0, &Src, nullptr);
}
}
}
else
{
+104 -40
View File
@@ -693,7 +693,7 @@ namespace vulkan_internal
wi::vector<VkImageView> subresources_dsv;
wi::vector<uint32_t> subresources_framebuffer_layercount;
VkSubresourceLayout subresourcelayout = {};
wi::vector<SubresourceData> mapped_subresources;
~Texture_Vulkan()
{
@@ -3460,7 +3460,7 @@ using namespace vulkan_internal;
buffer->internal_state = internal_state;
buffer->type = GPUResource::Type::BUFFER;
buffer->mapped_data = nullptr;
buffer->mapped_rowpitch = 0;
buffer->mapped_size = 0;
buffer->desc = *desc;
VkBufferCreateInfo bufferInfo = {};
@@ -3549,7 +3549,7 @@ using namespace vulkan_internal;
if (desc->usage == Usage::READBACK || desc->usage == Usage::UPLOAD)
{
buffer->mapped_data = internal_state->allocation->GetMappedData();
buffer->mapped_rowpitch = static_cast<uint32_t>(desc->size);
buffer->mapped_size = internal_state->allocation->GetSize();
}
if (bufferInfo.usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT)
@@ -3663,7 +3663,9 @@ using namespace vulkan_internal;
texture->internal_state = internal_state;
texture->type = GPUResource::Type::TEXTURE;
texture->mapped_data = nullptr;
texture->mapped_rowpitch = 0;
texture->mapped_size = 0;
texture->mapped_subresources = nullptr;
texture->mapped_subresource_count = 0;
texture->desc = *desc;
if (texture->desc.mip_levels == 0)
@@ -3678,7 +3680,7 @@ using namespace vulkan_internal;
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.extent.width = texture->desc.width;
imageInfo.extent.height = texture->desc.height;
imageInfo.extent.depth = 1;
imageInfo.extent.depth = texture->desc.depth;
imageInfo.format = _ConvertFormat(texture->desc.format);
imageInfo.arrayLayers = texture->desc.array_size;
imageInfo.mipLevels = texture->desc.mip_levels;
@@ -3742,7 +3744,6 @@ using namespace vulkan_internal;
break;
case TextureDesc::Type::TEXTURE_3D:
imageInfo.imageType = VK_IMAGE_TYPE_3D;
imageInfo.extent.depth = texture->desc.depth;
break;
default:
assert(0);
@@ -3755,12 +3756,23 @@ using namespace vulkan_internal;
{
VkBufferCreateInfo bufferInfo = {};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size =
imageInfo.extent.width *
imageInfo.extent.height *
imageInfo.extent.depth *
imageInfo.arrayLayers *
GetFormatStride(texture->desc.format);
bufferInfo.size = 0;
const uint32_t data_stride = GetFormatStride(texture->desc.format);
const uint32_t block_size = GetFormatBlockSize(texture->desc.format);
const uint32_t num_blocks_x = texture->desc.width / block_size;
const uint32_t num_blocks_y = texture->desc.height / block_size;
uint32_t mip_width = num_blocks_x;
uint32_t mip_height = num_blocks_y;
uint32_t mip_depth = texture->desc.depth;
for (uint32_t mip = 0; mip < texture->desc.mip_levels; ++mip)
{
bufferInfo.size += mip_width * mip_height * mip_depth;
mip_width = std::max(1u, mip_width / 2);
mip_height = std::max(1u, mip_height / 2);
mip_depth = std::max(1u, mip_depth / 2);
}
bufferInfo.size *= imageInfo.arrayLayers;
bufferInfo.size *= data_stride;
allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
if (texture->desc.usage == Usage::READBACK)
@@ -3778,18 +3790,43 @@ using namespace vulkan_internal;
assert(res == VK_SUCCESS);
imageInfo.tiling = VK_IMAGE_TILING_LINEAR;
imageInfo.mipLevels = 1; // set miplevels to 1 for linear tiling resource (this resource is just temporary)
VkImage image;
res = vkCreateImage(device, &imageInfo, nullptr, &image);
assert(res == VK_SUCCESS);
VkSubresourceLayout subresourcelayout = {};
VkImageSubresource subresource = {};
subresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
vkGetImageSubresourceLayout(device, image, &subresource, &internal_state->subresourcelayout);
vkGetImageSubresourceLayout(device, image, &subresource, &subresourcelayout);
if (texture->desc.usage == Usage::READBACK || texture->desc.usage == Usage::UPLOAD)
{
texture->mapped_data = internal_state->allocation->GetMappedData();
texture->mapped_rowpitch = (uint32_t)internal_state->subresourcelayout.rowPitch;
texture->mapped_size = internal_state->allocation->GetSize();
internal_state->mapped_subresources.resize(texture->desc.array_size* texture->desc.mip_levels);
size_t subresourceIndex = 0;
size_t subresourceDataOffset = 0;
for (uint32_t layer = 0; layer < texture->desc.array_size; ++layer)
{
uint32_t rowpitch = (uint32_t)subresourcelayout.rowPitch;
uint32_t slicepitch = (uint32_t)subresourcelayout.depthPitch;
uint32_t mip_width = num_blocks_x;
for (uint32_t mip = 0; mip < texture->desc.mip_levels; ++mip)
{
SubresourceData& subresourcedata = internal_state->mapped_subresources[subresourceIndex++];
subresourcedata.data_ptr = (uint8_t*)texture->mapped_data + subresourceDataOffset;
subresourcedata.row_pitch = rowpitch;
subresourcedata.slice_pitch = slicepitch;
subresourceDataOffset += std::max(rowpitch * mip_width, slicepitch);
rowpitch = std::max(data_stride, rowpitch / 2);
slicepitch = std::max(data_stride, slicepitch / 4); // squared reduction
mip_width = std::max(1u, mip_width / 2);
}
}
texture->mapped_subresources = internal_state->mapped_subresources.data();
texture->mapped_subresource_count = internal_state->mapped_subresources.size();
}
vkDestroyImage(device, image, nullptr);
@@ -7015,38 +7052,65 @@ using namespace vulkan_internal;
if (src_desc.usage == Usage::UPLOAD)
{
VkBufferImageCopy copy = {};
copy.imageExtent.width = dst_desc.width;
copy.imageExtent.height = dst_desc.height;
copy.imageExtent.depth = 1;
copy.imageExtent.width = dst_desc.width;
copy.imageSubresource.baseArrayLayer = 0;
copy.imageSubresource.layerCount = dst_desc.array_size;
copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copy.imageSubresource.layerCount = 1;
vkCmdCopyBufferToImage(
commandlist.GetCommandBuffer(),
internal_state_src->staging_resource,
internal_state_dst->resource,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&copy
);
const uint32_t data_stride = GetFormatStride(dst_desc.format);
uint32_t mip_width = dst_desc.width;
uint32_t mip_height = dst_desc.height;
uint32_t mip_depth = dst_desc.depth;
for (uint32_t mip = 0; mip < dst_desc.mip_levels; ++mip)
{
copy.imageExtent.width = mip_width;
copy.imageExtent.height = mip_height;
copy.imageExtent.depth = mip_depth;
copy.imageSubresource.mipLevel = mip;
vkCmdCopyBufferToImage(
commandlist.GetCommandBuffer(),
internal_state_src->staging_resource,
internal_state_dst->resource,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&copy
);
copy.bufferOffset += mip_width * mip_height * mip_depth * data_stride;
mip_width = std::max(1u, mip_width / 2);
mip_height = std::max(1u, mip_height / 2);
mip_depth = std::max(1u, mip_depth / 2);
}
}
else if (dst_desc.usage == Usage::READBACK)
{
VkBufferImageCopy copy = {};
copy.imageExtent.width = src_desc.width;
copy.imageExtent.height = src_desc.height;
copy.imageExtent.depth = 1;
copy.imageExtent.width = src_desc.width;
copy.imageSubresource.baseArrayLayer = 0;
copy.imageSubresource.layerCount = dst_desc.array_size;
copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copy.imageSubresource.layerCount = 1;
vkCmdCopyImageToBuffer(
commandlist.GetCommandBuffer(),
internal_state_src->resource,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
internal_state_dst->staging_resource,
1,
&copy
);
const uint32_t data_stride = GetFormatStride(dst_desc.format);
uint32_t mip_width = dst_desc.width;
uint32_t mip_height = dst_desc.height;
uint32_t mip_depth = dst_desc.depth;
for (uint32_t mip = 0; mip < dst_desc.mip_levels; ++mip)
{
copy.imageExtent.width = mip_width;
copy.imageExtent.height = mip_height;
copy.imageExtent.depth = mip_depth;
copy.imageSubresource.mipLevel = mip;
vkCmdCopyImageToBuffer(
commandlist.GetCommandBuffer(),
internal_state_src->resource,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
internal_state_dst->staging_resource,
1,
&copy
);
copy.bufferOffset += mip_width * mip_height * mip_depth * data_stride;
mip_width = std::max(1u, mip_width / 2);
mip_height = std::max(1u, mip_height / 2);
mip_depth = std::max(1u, mip_depth / 2);
}
}
else
{
+94 -26
View File
@@ -121,7 +121,6 @@ namespace wi::helper
Texture stagingTex;
TextureDesc staging_desc = desc;
staging_desc.usage = Usage::READBACK;
staging_desc.mip_levels = 1;
staging_desc.layout = ResourceState::COPY_DST;
staging_desc.bind_flags = BindFlag::NONE;
staging_desc.misc_flags = ResourceMiscFlag::NONE;
@@ -149,33 +148,48 @@ namespace wi::helper
device->SubmitCommandLists();
device->WaitForGPU();
desc.width /= GetFormatBlockSize(desc.format);
desc.height /= GetFormatBlockSize(desc.format);
uint32_t data_count = desc.width * desc.height;
uint32_t data_stride = GetFormatStride(desc.format);
uint32_t data_size = data_count * data_stride;
texturedata.clear();
texturedata.resize(data_size);
if (stagingTex.mapped_data != nullptr)
{
if (stagingTex.mapped_rowpitch / data_stride != desc.width)
texturedata.resize(stagingTex.mapped_size);
const uint32_t data_stride = GetFormatStride(desc.format);
const uint32_t block_size = GetFormatBlockSize(desc.format);
const uint32_t num_blocks_x = desc.width / block_size;
const uint32_t num_blocks_y = desc.height / block_size;
size_t cpy_offset = 0;
size_t subresourceIndex = 0;
for (uint32_t layer = 0; layer < desc.array_size; ++layer)
{
// Copy padded texture row by row:
const uint32_t cpysize = desc.width * data_stride;
for (uint32_t i = 0; i < desc.height; ++i)
uint32_t mip_width = num_blocks_x;
uint32_t mip_height = num_blocks_y;
uint32_t mip_depth = desc.depth;
for (uint32_t mip = 0; mip < desc.mip_levels; ++mip)
{
void* src = (void*)((size_t)stagingTex.mapped_data + size_t(i * stagingTex.mapped_rowpitch));
void* dst = (void*)((size_t)texturedata.data() + size_t(i * cpysize));
memcpy(dst, src, cpysize);
assert(subresourceIndex < stagingTex.mapped_subresource_count);
const SubresourceData& subresourcedata = stagingTex.mapped_subresources[subresourceIndex++];
const size_t dst_rowpitch = mip_width * data_stride;
for (uint32_t z = 0; z < mip_depth; ++z)
{
uint8_t* dst_slice = texturedata.data() + cpy_offset;
uint8_t* src_slice = (uint8_t*)subresourcedata.data_ptr + subresourcedata.slice_pitch * z;
for (uint32_t i = 0; i < mip_height; ++i)
{
std::memcpy(
dst_slice + i * dst_rowpitch,
src_slice + i * subresourcedata.row_pitch,
subresourcedata.row_pitch
);
}
cpy_offset += mip_height * dst_rowpitch;
}
mip_width = std::max(1u, mip_width / 2);
mip_height = std::max(1u, mip_height / 2);
mip_depth = std::max(1u, mip_depth / 2);
}
}
else
{
// Copy whole
std::memcpy(texturedata.data(), stagingTex.mapped_data, texturedata.size());
}
}
else
{
@@ -200,12 +214,40 @@ namespace wi::helper
bool saveTextureToMemoryFile(const wi::vector<uint8_t>& texturedata, const wi::graphics::TextureDesc& desc, const std::string& fileExtension, wi::vector<uint8_t>& filedata)
{
using namespace wi::graphics;
uint32_t data_count = desc.width * desc.height;
const uint32_t data_stride = GetFormatStride(desc.format);
struct MipDesc
{
const uint8_t* address = nullptr;
uint32_t width = 0;
uint32_t height = 0;
uint32_t depth = 0;
};
wi::vector<MipDesc> mips;
mips.reserve(desc.mip_levels);
uint32_t data_count = 0;
uint32_t mip_width = desc.width;
uint32_t mip_height = desc.height;
uint32_t mip_depth = desc.depth;
for (uint32_t mip = 0; mip < desc.mip_levels; ++mip)
{
MipDesc& mipdesc = mips.emplace_back();
mipdesc.address = texturedata.data() + data_count * data_stride;
data_count += mip_width * mip_height * mip_depth;
mipdesc.width = mip_width;
mipdesc.height = mip_height;
mipdesc.depth = mip_depth;
mip_width = std::max(1u, mip_width / 2);
mip_height = std::max(1u, mip_height / 2);
mip_depth = std::max(1u, mip_depth / 2);
}
std::string extension = wi::helper::toUpper(fileExtension);
bool basis = !extension.compare("BASIS");
bool ktx2 = !extension.compare("KTX2");
basisu::image basis_image;
basisu::vector<basisu::image> basis_mipmaps;
if (desc.format == Format::R10G10B10A2_UNORM)
{
@@ -343,9 +385,24 @@ namespace wi::helper
if (basis_image.get_total_pixels() == 0)
{
basis_image.init(texturedata.data(), desc.width, desc.height, 4);
if (desc.mip_levels > 1)
{
basis_mipmaps.reserve(desc.mip_levels - 1);
for (uint32_t mip = 1; mip < desc.mip_levels; ++mip)
{
basisu::image basis_mip;
const MipDesc& mipdesc = mips[mip];
basis_mip.init(mipdesc.address, mipdesc.width, mipdesc.height, 4);
basis_mipmaps.push_back(basis_mip);
}
}
}
basisu::basis_compressor_params params;
params.m_source_images.push_back(basis_image);
if (desc.mip_levels > 1)
{
params.m_source_mipmap_images.push_back(basis_mipmaps);
}
if (ktx2)
{
params.m_create_ktx2_file = true;
@@ -359,7 +416,10 @@ namespace wi::helper
#else
params.m_compression_level = basisu::BASISU_MAX_COMPRESSION_LEVEL;
#endif
params.m_mip_gen = true;
// Disable CPU mipmap generation:
// instead we provide mipmap data that was downloaded from the GPU with m_source_mipmap_images.
// This is better, because engine specific mipgen options will be retained, such as coverage preserving mipmaps
params.m_mip_gen = false;
params.m_pSel_codebook = &g_basis_global_codebook;
params.m_quality_level = basisu::BASISU_QUALITY_MAX;
params.m_multithreading = true;
@@ -387,6 +447,11 @@ namespace wi::helper
return true;
}
}
else
{
wi::backlog::post("basisu::basis_compressor::process() failure!", wi::backlog::LogLevel::Error);
assert(0);
}
}
return false;
}
@@ -408,21 +473,24 @@ namespace wi::helper
src_data = basis_image.get_ptr();
}
static int mip_request = 0; // you can use this while debugging to write specific mip level to file (todo: option param?)
const MipDesc& mip = mips[mip_request];
if (!extension.compare("JPG") || !extension.compare("JPEG"))
{
write_result = stbi_write_jpg_to_func(func, &filedata, (int)desc.width, (int)desc.height, 4, src_data, 100);
write_result = stbi_write_jpg_to_func(func, &filedata, (int)mip.width, (int)mip.height, 4, mip.address, 100);
}
else if (!extension.compare("PNG"))
{
write_result = stbi_write_png_to_func(func, &filedata, (int)desc.width, (int)desc.height, 4, src_data, 0);
write_result = stbi_write_png_to_func(func, &filedata, (int)mip.width, (int)mip.height, 4, mip.address, 0);
}
else if (!extension.compare("TGA"))
{
write_result = stbi_write_tga_to_func(func, &filedata, (int)desc.width, (int)desc.height, 4, src_data);
write_result = stbi_write_tga_to_func(func, &filedata, (int)mip.width, (int)mip.height, 4, mip.address);
}
else if (!extension.compare("BMP"))
{
write_result = stbi_write_bmp_to_func(func, &filedata, (int)desc.width, (int)desc.height, 4, src_data);
write_result = stbi_write_bmp_to_func(func, &filedata, (int)mip.width, (int)mip.height, 4, mip.address);
}
else
{
+39 -11
View File
@@ -3,6 +3,7 @@
#include "wiHelper.h"
#include "wiTextureHelper.h"
#include "wiUnorderedMap.h"
#include "wiBacklog.h"
#include "Utility/stb_image.h"
#include "Utility/qoi.h"
@@ -235,11 +236,14 @@ namespace wi
{
// all subresources will use one allocation for transcoder destination, so compute combined size:
size_t transcoded_data_size = 0;
for (uint32_t layer = 0; layer < std::max(1u, transcoder.get_layers()); ++layer)
const uint32_t layers = std::max(1u, transcoder.get_layers());
const uint32_t faces = transcoder.get_faces();
const uint32_t levels = transcoder.get_levels();
for (uint32_t layer = 0; layer < layers; ++layer)
{
for (uint32_t face = 0; face < transcoder.get_faces(); ++face)
for (uint32_t face = 0; face < faces; ++face)
{
for (uint32_t mip = 0; mip < transcoder.get_levels(); ++mip)
for (uint32_t mip = 0; mip < levels; ++mip)
{
basist::ktx2_image_level_info level_info;
if (transcoder.get_image_level_info(level_info, mip, layer, face))
@@ -249,15 +253,15 @@ namespace wi
}
}
}
wi::vector<uint8_t*> transcoded_data(transcoded_data_size);
wi::vector<uint8_t> transcoded_data(transcoded_data_size);
wi::vector<SubresourceData> InitData;
size_t transcoded_data_offset = 0;
for (uint32_t layer = 0; layer < std::max(1u, transcoder.get_layers()); ++layer)
for (uint32_t layer = 0; layer < layers; ++layer)
{
for (uint32_t face = 0; face < transcoder.get_faces(); ++face)
for (uint32_t face = 0; face < faces; ++face)
{
for (uint32_t mip = 0; mip < transcoder.get_levels(); ++mip)
for (uint32_t mip = 0; mip < levels; ++mip)
{
basist::ktx2_image_level_info level_info;
if (transcoder.get_image_level_info(level_info, mip, layer, face))
@@ -265,7 +269,9 @@ namespace wi
void* data_ptr = transcoded_data.data() + transcoded_data_offset;
transcoded_data_offset += level_info.m_total_blocks * bytes_per_block;
if (transcoder.transcode_image_level(
mip, layer, face,
mip,
layer,
face,
data_ptr,
level_info.m_total_blocks,
fmt
@@ -277,6 +283,16 @@ namespace wi
subresourceData.slice_pitch = subresourceData.row_pitch * level_info.m_num_blocks_y;
InitData.push_back(subresourceData);
}
else
{
wi::backlog::post("KTX2 transcoding error while loading image!", wi::backlog::LogLevel::Error);
assert(0);
}
}
else
{
wi::backlog::post("KTX2 transcoding error while loading image level info!", wi::backlog::LogLevel::Error);
assert(0);
}
}
}
@@ -334,7 +350,7 @@ namespace wi
transcoded_data_size += level_info.m_total_blocks * bytes_per_block;
}
}
wi::vector<uint8_t*> transcoded_data(transcoded_data_size);
wi::vector<uint8_t> transcoded_data(transcoded_data_size);
wi::vector<SubresourceData> InitData;
size_t transcoded_data_offset = 0;
@@ -346,10 +362,12 @@ namespace wi
void* data_ptr = transcoded_data.data() + transcoded_data_offset;
transcoded_data_offset += level_info.m_total_blocks * bytes_per_block;
if (transcoder.transcode_image_level(
filedata, (uint32_t)filesize, image_index,
filedata,
(uint32_t)filesize,
image_index,
mip,
data_ptr,
info.m_total_blocks,
level_info.m_total_blocks,
fmt
))
{
@@ -359,6 +377,16 @@ namespace wi
subresourceData.slice_pitch = subresourceData.row_pitch * level_info.m_num_blocks_y;
InitData.push_back(subresourceData);
}
else
{
wi::backlog::post("BASIS transcoding error while loading image!", wi::backlog::LogLevel::Error);
assert(0);
}
}
else
{
wi::backlog::post("BASIS transcoding error while loading image level info!", wi::backlog::LogLevel::Error);
assert(0);
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ namespace wi::version
// minor features, major updates, breaking compatibility changes
const int minor = 60;
// minor bug fixes, alterations, refactors, updates
const int revision = 87;
const int revision = 88;
const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);