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:
Binary file not shown.
Binary file not shown.
@@ -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; }
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
©
|
||||
);
|
||||
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.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,
|
||||
©
|
||||
);
|
||||
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.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
@@ -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
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user