Vortek Internals: Part 3 - Addendum for vkCreateImageView
AKA: I was dumb and spent a whole week writing 3 separate implementations of texture decompression, trying 50 permutations of vkCmdPipelineBarriers
before realizing that I forgot to account for vkCreateImageView
vt_handle_vkCreateImageView
In addition to intercepting vkCreateImage
, vortek must also intercept all usages of vkCreateImageView
in order to present specific views of a texture that may be rendered with the BCn format. Vortek handles this in a pretty straightforward fashion:
void vt_handle_vkCreateImageView(VtContext* ctx) {
char* serialized_payload = ctx->current_command_data_buffer;
// Usual decompression loop, which includes desesrializing the VkImageViewCreateInfo* createInfo
...
// 2. Check for Managed Image and Correct Format
if (ctx->texture_decoder != NULL && isCompressedFormat(createInfo.format)) {
createInfo.format = VK_FORMAT_B8G8R8A8_UNORM; // 0x2c
createInfo.subresourceRange.levelCount = 1; // Force mip-level to be 1
}
// 3. Native Vulkan Call
VkImageView imageView;
VkResult result = vkCreateImageView(device, &createInfo, NULL, &imageView);
// 4. Write Response
RingBuffer_write(ctx->pResponseBuffer, &result, sizeof(VkResult));
RingBuffer_write(ctx->pResponseBuffer, &imageView, sizeof(VkImageView));
}
For now, Vortek doesn’t support mip-chains (though in theory, this shouldn’t be hard to support), and it may lead to strange low-res texture rendering as it will always take the lowest resolution mip from dxvk (which submits its mipchains from highest to lowest resolution)
vt_handle_vkGetPhysicalDeviceImageFormatProperties
Additionally, we must advertise support for BCn textures by responding to format property queries. This is typically done via vkGetPhysicalDeviceImageFormatProperties
, which vortek handles by returning the following VkImageFormatProperties
:
VkResult getCompressedImageFormatProperties(VkFormat format, VkImageFormatProperties* pImageFormatProperties) {
if (format >= VK_FORMAT_BC1_RGB_UNORM_BLOCK && format <= VK_FORMAT_BC5_SNORM_BLOCK) {
// *param_2 = 0x400000004000;
pImageFormatProperties->maxExtent.width = 16384; // offset: +0x00, value: 0x4000
pImageFormatProperties->maxExtent.height = 16384; // offset: +0x04, value: 0x4000
// param_2[1] = 0xf00000001;
pImageFormatProperties->maxExtent.depth = 1; // offset: +0x08, value: 0x1
pImageFormatProperties->maxMipLevels = 15; // offset: +0x0C, value: 0xf
// param_2[2] = 0x100000800;
pImageFormatProperties->maxArrayLayers = 2048; // offset: +0x10, value: 0x800
pImageFormatProperties->sampleCounts = VK_SAMPLE_COUNT_1_BIT; // offset: +0x14, VK_SAMPLE_COUNT_1_BIT = 1
// param_2[3] = 0x80000000;
pImageFormatProperties->maxResourceSize = 2147483648; // offset: +0x18, value: 0x80000000 (2GB)
return VK_SUCCESS; // 0
}
return VK_ERROR_FORMAT_NOT_SUPPORTED; // -11 (0xfffffff5)
}
void vt_handle_vkGetPhysicalDeviceImageFormatProperties(VortekContext* ctx) {
// 1. Argument Deserialization
...
VkImageFormatProperties imageFormatProperties = {0};
// `(*_DAT_0013c6b8)` is the function pointer to vkGetPhysicalDeviceImageFormatProperties.
VkResult result = vkGetPhysicalDeviceImageFormatProperties(
physicalDeviceObject,
format,
type,
tiling,
usage,
flags,
&imageFormatProperties
);
// If the driver doesn't support the format, check if we can decode it ourselves.
if (result == VK_ERROR_FORMAT_NOT_SUPPORTED) { // result == -11
if (isCompressedFormat(format)) {
// Provide our own dummy properties and report success.
result = getCompressedImageFormatProperties(format, &imageFormatProperties);
}
}
RingBuffer_write(ctx->pResponseBuffer, &result, sizeof(VkResult));
RingBuffer_write(ctx->pResponseBuffer, &imageFormatProperties, sizeof(VkImageFormatProperties));
}