2019-12-16 03:28:01 +00:00
|
|
|
#include "d3d9_format_helpers.h"
|
|
|
|
|
|
|
|
#include <d3d9_convert_yuy2_uyvy.h>
|
2020-02-24 06:39:25 +00:00
|
|
|
#include <d3d9_convert_l6v5u5.h>
|
2020-03-08 23:22:52 +00:00
|
|
|
#include <d3d9_convert_x8l8v8u8.h>
|
2020-03-08 23:32:33 +00:00
|
|
|
#include <d3d9_convert_a2w10v10u10.h>
|
2022-11-05 17:57:57 +01:00
|
|
|
#include <d3d9_convert_w11v11u10.h>
|
2020-08-07 09:37:55 +01:00
|
|
|
#include <d3d9_convert_nv12.h>
|
2020-08-16 15:13:30 +01:00
|
|
|
#include <d3d9_convert_yv12.h>
|
2019-12-16 03:28:01 +00:00
|
|
|
|
|
|
|
namespace dxvk {
|
|
|
|
|
|
|
|
D3D9FormatHelper::D3D9FormatHelper(const Rc<DxvkDevice>& device)
|
2024-10-22 12:48:39 +02:00
|
|
|
: m_device (device)
|
|
|
|
, m_setLayout (CreateSetLayout())
|
|
|
|
, m_pipelineLayout (CreatePipelineLayout()) {
|
|
|
|
InitPipelines();
|
2019-12-16 03:28:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-10-22 12:48:39 +02:00
|
|
|
D3D9FormatHelper::~D3D9FormatHelper() {
|
|
|
|
auto vk = m_device->vkd();
|
|
|
|
|
|
|
|
for (auto& p : m_pipelines)
|
|
|
|
vk->vkDestroyPipeline(vk->device(), p, nullptr);
|
2024-11-03 10:53:05 +01:00
|
|
|
|
|
|
|
vk->vkDestroyDescriptorSetLayout(vk->device(), m_setLayout, nullptr);
|
|
|
|
vk->vkDestroyPipelineLayout(vk->device(), m_pipelineLayout, nullptr);
|
2020-02-29 14:00:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-02-10 18:26:54 +00:00
|
|
|
void D3D9FormatHelper::ConvertFormat(
|
2024-10-22 12:48:39 +02:00
|
|
|
const DxvkContextObjects& ctx,
|
2020-02-10 18:26:54 +00:00
|
|
|
D3D9_CONVERSION_FORMAT_INFO conversionFormat,
|
|
|
|
const Rc<DxvkImage>& dstImage,
|
|
|
|
VkImageSubresourceLayers dstSubresource,
|
2021-06-12 12:14:18 +02:00
|
|
|
const DxvkBufferSlice& srcSlice) {
|
2020-02-10 18:26:54 +00:00
|
|
|
switch (conversionFormat.FormatType) {
|
|
|
|
case D3D9ConversionFormat_YUY2:
|
2020-03-02 03:58:28 +00:00
|
|
|
case D3D9ConversionFormat_UYVY: {
|
2024-10-22 12:48:39 +02:00
|
|
|
ConvertGenericFormat(ctx, conversionFormat, dstImage, dstSubresource, srcSlice, VK_FORMAT_R32_UINT, { 2u, 1u });
|
2020-02-24 06:39:56 +00:00
|
|
|
break;
|
2020-03-02 03:58:28 +00:00
|
|
|
}
|
2020-03-08 23:22:52 +00:00
|
|
|
|
2020-08-07 09:37:55 +01:00
|
|
|
case D3D9ConversionFormat_NV12:
|
2024-10-22 12:48:39 +02:00
|
|
|
ConvertGenericFormat(ctx, conversionFormat, dstImage, dstSubresource, srcSlice, VK_FORMAT_R16_UINT, { 2u, 1u });
|
2020-08-07 09:37:55 +01:00
|
|
|
break;
|
|
|
|
|
2020-08-16 15:13:30 +01:00
|
|
|
case D3D9ConversionFormat_YV12:
|
2024-10-22 12:48:39 +02:00
|
|
|
ConvertGenericFormat(ctx, conversionFormat, dstImage, dstSubresource, srcSlice, VK_FORMAT_R8_UINT, { 1u, 1u });
|
2020-08-16 15:13:30 +01:00
|
|
|
break;
|
|
|
|
|
2020-02-24 06:39:25 +00:00
|
|
|
case D3D9ConversionFormat_L6V5U5:
|
2024-10-22 12:48:39 +02:00
|
|
|
ConvertGenericFormat(ctx, conversionFormat, dstImage, dstSubresource, srcSlice, VK_FORMAT_R16_UINT, { 1u, 1u });
|
2020-02-24 06:39:56 +00:00
|
|
|
break;
|
2020-03-08 23:22:52 +00:00
|
|
|
|
|
|
|
case D3D9ConversionFormat_X8L8V8U8:
|
2024-10-22 12:48:39 +02:00
|
|
|
ConvertGenericFormat(ctx, conversionFormat, dstImage, dstSubresource, srcSlice, VK_FORMAT_R32_UINT, { 1u, 1u });
|
2020-03-08 23:22:52 +00:00
|
|
|
break;
|
|
|
|
|
2020-03-08 23:32:33 +00:00
|
|
|
case D3D9ConversionFormat_A2W10V10U10:
|
2024-10-22 12:48:39 +02:00
|
|
|
ConvertGenericFormat(ctx, conversionFormat, dstImage, dstSubresource, srcSlice, VK_FORMAT_R32_UINT, { 1u, 1u });
|
2020-03-08 23:32:33 +00:00
|
|
|
break;
|
|
|
|
|
2022-11-05 17:57:57 +01:00
|
|
|
case D3D9ConversionFormat_W11V11U10:
|
2024-10-22 12:48:39 +02:00
|
|
|
ConvertGenericFormat(ctx, conversionFormat, dstImage, dstSubresource, srcSlice, VK_FORMAT_R32_UINT, { 1u, 1u });
|
2022-11-05 17:57:57 +01:00
|
|
|
break;
|
|
|
|
|
2020-02-10 18:26:54 +00:00
|
|
|
default:
|
|
|
|
Logger::warn("Unimplemented format conversion");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-02-24 06:39:25 +00:00
|
|
|
void D3D9FormatHelper::ConvertGenericFormat(
|
2024-10-22 12:48:39 +02:00
|
|
|
const DxvkContextObjects& ctx,
|
2020-02-24 06:39:25 +00:00
|
|
|
D3D9_CONVERSION_FORMAT_INFO videoFormat,
|
|
|
|
const Rc<DxvkImage>& dstImage,
|
|
|
|
VkImageSubresourceLayers dstSubresource,
|
2021-06-12 12:14:18 +02:00
|
|
|
const DxvkBufferSlice& srcSlice,
|
2020-03-02 03:58:28 +00:00
|
|
|
VkFormat bufferFormat,
|
2020-08-07 09:52:25 +01:00
|
|
|
VkExtent2D macroPixelRun) {
|
2024-09-28 01:50:23 +02:00
|
|
|
DxvkImageViewKey imageViewInfo;
|
|
|
|
imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
2019-12-16 03:28:01 +00:00
|
|
|
imageViewInfo.format = dstImage->info().format;
|
|
|
|
imageViewInfo.usage = VK_IMAGE_USAGE_STORAGE_BIT;
|
2024-09-28 01:50:23 +02:00
|
|
|
imageViewInfo.aspects = dstSubresource.aspectMask;
|
|
|
|
imageViewInfo.mipIndex = dstSubresource.mipLevel;
|
|
|
|
imageViewInfo.mipCount = 1;
|
|
|
|
imageViewInfo.layerIndex = dstSubresource.baseArrayLayer;
|
|
|
|
imageViewInfo.layerCount = dstSubresource.layerCount;
|
2024-09-28 00:02:17 +02:00
|
|
|
auto tmpImageView = dstImage->createView(imageViewInfo);
|
2019-12-16 03:28:01 +00:00
|
|
|
|
|
|
|
VkExtent3D imageExtent = dstImage->mipLevelExtent(dstSubresource.mipLevel);
|
2020-08-07 09:52:25 +01:00
|
|
|
imageExtent = VkExtent3D{ imageExtent.width / macroPixelRun.width,
|
|
|
|
imageExtent.height / macroPixelRun.height,
|
2019-12-16 03:28:01 +00:00
|
|
|
1 };
|
|
|
|
|
2024-09-28 01:20:26 +02:00
|
|
|
DxvkBufferViewKey bufferViewInfo;
|
|
|
|
bufferViewInfo.format = bufferFormat;
|
|
|
|
bufferViewInfo.offset = srcSlice.offset();
|
|
|
|
bufferViewInfo.size = srcSlice.length();
|
2024-09-13 14:16:27 +02:00
|
|
|
bufferViewInfo.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
|
2024-09-23 22:10:12 +02:00
|
|
|
auto tmpBufferView = srcSlice.buffer()->createView(bufferViewInfo);
|
2019-12-16 03:28:01 +00:00
|
|
|
|
2024-10-22 12:48:39 +02:00
|
|
|
VkDescriptorSet set = ctx.descriptorPool->alloc(m_setLayout);
|
|
|
|
|
|
|
|
VkDescriptorImageInfo imageDescriptor = { };
|
|
|
|
imageDescriptor.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
|
|
imageDescriptor.imageView = tmpImageView->handle();
|
|
|
|
|
|
|
|
VkBufferView bufferViewHandle = tmpBufferView->handle();
|
|
|
|
|
|
|
|
std::array<VkWriteDescriptorSet, 2> descriptorWrites = {{
|
|
|
|
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
|
|
|
|
set, 0, 0, 1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &imageDescriptor },
|
|
|
|
{ VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr,
|
|
|
|
set, 1, 0, 1, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, nullptr, nullptr, &bufferViewHandle },
|
|
|
|
}};
|
|
|
|
|
|
|
|
ctx.cmd->updateDescriptorSets(
|
|
|
|
descriptorWrites.size(),
|
|
|
|
descriptorWrites.data());
|
|
|
|
|
|
|
|
ctx.cmd->cmdBindPipeline(DxvkCmdBuffer::ExecBuffer,
|
|
|
|
VK_PIPELINE_BIND_POINT_COMPUTE, m_pipelines[videoFormat.FormatType]);
|
|
|
|
ctx.cmd->cmdBindDescriptorSet(DxvkCmdBuffer::ExecBuffer,
|
|
|
|
VK_PIPELINE_BIND_POINT_COMPUTE, m_pipelineLayout, set, 0, nullptr);
|
|
|
|
ctx.cmd->cmdPushConstants(DxvkCmdBuffer::ExecBuffer, m_pipelineLayout,
|
|
|
|
VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(VkExtent2D), &imageExtent);
|
|
|
|
ctx.cmd->cmdDispatch(DxvkCmdBuffer::ExecBuffer,
|
|
|
|
((imageExtent.width + 7u) / 8u),
|
|
|
|
((imageExtent.height + 7u) / 8u),
|
|
|
|
1u);
|
|
|
|
|
|
|
|
// We can reasonably assume that the image is in GENERAL layout anyway
|
|
|
|
VkMemoryBarrier2 memoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };
|
|
|
|
memoryBarrier.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;
|
|
|
|
memoryBarrier.srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT;
|
|
|
|
memoryBarrier.dstStageMask = dstImage->info().stages | srcSlice.buffer()->info().stages;
|
|
|
|
memoryBarrier.dstAccessMask = dstImage->info().access | srcSlice.buffer()->info().access;
|
|
|
|
|
|
|
|
VkDependencyInfo depInfo = { VK_STRUCTURE_TYPE_DEPENDENCY_INFO };
|
|
|
|
depInfo.memoryBarrierCount = 1u;
|
|
|
|
depInfo.pMemoryBarriers = &memoryBarrier;
|
|
|
|
|
|
|
|
ctx.cmd->cmdPipelineBarrier(DxvkCmdBuffer::ExecBuffer, &depInfo);
|
|
|
|
|
|
|
|
ctx.cmd->track(tmpImageView->image(), DxvkAccess::Write);
|
|
|
|
ctx.cmd->track(tmpBufferView->buffer(), DxvkAccess::Read);
|
2019-12-16 03:28:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-10-22 12:48:39 +02:00
|
|
|
void D3D9FormatHelper::InitPipelines() {
|
|
|
|
m_pipelines[D3D9ConversionFormat_YUY2] = CreatePipeline(sizeof(d3d9_convert_yuy2_uyvy), d3d9_convert_yuy2_uyvy, 0);
|
|
|
|
m_pipelines[D3D9ConversionFormat_UYVY] = CreatePipeline(sizeof(d3d9_convert_yuy2_uyvy), d3d9_convert_yuy2_uyvy, 1);
|
|
|
|
m_pipelines[D3D9ConversionFormat_L6V5U5] = CreatePipeline(sizeof(d3d9_convert_l6v5u5), d3d9_convert_l6v5u5, 0);
|
|
|
|
m_pipelines[D3D9ConversionFormat_X8L8V8U8] = CreatePipeline(sizeof(d3d9_convert_x8l8v8u8), d3d9_convert_x8l8v8u8, 0);
|
|
|
|
m_pipelines[D3D9ConversionFormat_A2W10V10U10] = CreatePipeline(sizeof(d3d9_convert_a2w10v10u10), d3d9_convert_a2w10v10u10, 0);
|
|
|
|
m_pipelines[D3D9ConversionFormat_W11V11U10] = CreatePipeline(sizeof(d3d9_convert_w11v11u10), d3d9_convert_w11v11u10, 0);
|
|
|
|
m_pipelines[D3D9ConversionFormat_NV12] = CreatePipeline(sizeof(d3d9_convert_nv12), d3d9_convert_nv12, 0);
|
|
|
|
m_pipelines[D3D9ConversionFormat_YV12] = CreatePipeline(sizeof(d3d9_convert_yv12), d3d9_convert_yv12, 0);
|
2019-12-16 03:28:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-10-22 12:48:39 +02:00
|
|
|
VkDescriptorSetLayout D3D9FormatHelper::CreateSetLayout() {
|
|
|
|
auto vk = m_device->vkd();
|
|
|
|
|
|
|
|
static const std::array<VkDescriptorSetLayoutBinding, 2> bindings = {{
|
|
|
|
{ 0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT },
|
|
|
|
{ 1, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT },
|
|
|
|
}};
|
2022-06-21 11:05:09 +02:00
|
|
|
|
2024-10-22 12:48:39 +02:00
|
|
|
VkDescriptorSetLayoutCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
|
2022-06-21 11:05:09 +02:00
|
|
|
info.bindingCount = bindings.size();
|
2024-10-22 12:48:39 +02:00
|
|
|
info.pBindings = bindings.data();
|
|
|
|
|
|
|
|
VkDescriptorSetLayout layout = VK_NULL_HANDLE;
|
|
|
|
VkResult vr = vk->vkCreateDescriptorSetLayout(vk->device(), &info, nullptr, &layout);
|
|
|
|
|
|
|
|
if (vr != VK_SUCCESS)
|
|
|
|
throw DxvkError(str::format("Failed to create format conversion descriptor set layout: ", vr));
|
|
|
|
|
|
|
|
return layout;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VkPipelineLayout D3D9FormatHelper::CreatePipelineLayout() {
|
|
|
|
auto vk = m_device->vkd();
|
|
|
|
|
|
|
|
VkPushConstantRange pushConstants = { };
|
|
|
|
pushConstants.size = sizeof(VkExtent2D);
|
|
|
|
pushConstants.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
|
|
|
|
|
|
VkPipelineLayoutCreateInfo info = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
|
|
|
|
info.setLayoutCount = 1u;
|
|
|
|
info.pSetLayouts = &m_setLayout;
|
|
|
|
info.pushConstantRangeCount = 1u;
|
|
|
|
info.pPushConstantRanges = &pushConstants;
|
|
|
|
|
|
|
|
VkPipelineLayout layout = VK_NULL_HANDLE;
|
|
|
|
VkResult vr = vk->vkCreatePipelineLayout(vk->device(), &info, nullptr, &layout);
|
2022-04-09 14:20:27 +02:00
|
|
|
|
2024-10-22 12:48:39 +02:00
|
|
|
if (vr != VK_SUCCESS)
|
|
|
|
throw DxvkError(str::format("Failed to create format conversion pipeline layout: ", vr));
|
|
|
|
|
|
|
|
return layout;
|
2019-12-16 03:28:01 +00:00
|
|
|
}
|
|
|
|
|
2020-02-29 14:00:15 +00:00
|
|
|
|
2024-10-22 12:48:39 +02:00
|
|
|
VkPipeline D3D9FormatHelper::CreatePipeline(size_t size, const uint32_t* code, uint32_t specConstant) {
|
|
|
|
auto vk = m_device->vkd();
|
|
|
|
|
|
|
|
VkSpecializationMapEntry specEntry = { };
|
|
|
|
specEntry.size = sizeof(specConstant);
|
|
|
|
|
|
|
|
VkSpecializationInfo specInfo = { };
|
|
|
|
specInfo.mapEntryCount = 1;
|
|
|
|
specInfo.pMapEntries = &specEntry;
|
|
|
|
specInfo.dataSize = sizeof(specConstant);
|
|
|
|
specInfo.pData = &specConstant;
|
|
|
|
|
|
|
|
VkComputePipelineCreateInfo pipelineInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO };
|
|
|
|
pipelineInfo.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
|
|
pipelineInfo.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
|
|
pipelineInfo.stage.pName = "main";
|
|
|
|
pipelineInfo.stage.pSpecializationInfo = &specInfo;
|
|
|
|
pipelineInfo.layout = m_pipelineLayout;
|
|
|
|
pipelineInfo.basePipelineIndex = -1;
|
|
|
|
|
|
|
|
VkShaderModuleCreateInfo moduleInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
|
|
|
|
moduleInfo.codeSize = size;
|
|
|
|
moduleInfo.pCode = code;
|
|
|
|
|
|
|
|
if (m_device->features().khrMaintenance5.maintenance5
|
|
|
|
|| m_device->features().extGraphicsPipelineLibrary.graphicsPipelineLibrary) {
|
|
|
|
pipelineInfo.stage.pNext = &moduleInfo;
|
|
|
|
} else {
|
|
|
|
VkResult vr = vk->vkCreateShaderModule(vk->device(),
|
|
|
|
&moduleInfo, nullptr, &pipelineInfo.stage.module);
|
|
|
|
|
|
|
|
if (vr != VK_SUCCESS)
|
|
|
|
throw DxvkError(str::format("Failed to create format conversion shader module: ", vr));
|
|
|
|
}
|
|
|
|
|
|
|
|
VkPipeline pipeline = VK_NULL_HANDLE;
|
|
|
|
VkResult vr = vk->vkCreateComputePipelines(vk->device(),
|
|
|
|
VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &pipeline);
|
|
|
|
|
|
|
|
vk->vkDestroyShaderModule(vk->device(), pipelineInfo.stage.module, nullptr);
|
|
|
|
|
|
|
|
if (vr != VK_SUCCESS)
|
|
|
|
throw DxvkError(str::format("Failed to create format conversion pipeline: ", vr));
|
|
|
|
|
|
|
|
return pipeline;
|
2020-02-29 14:00:15 +00:00
|
|
|
}
|
|
|
|
|
2019-12-16 03:28:01 +00:00
|
|
|
}
|