125 changes: 56 additions & 69 deletions src/d3d11/d3d11_video.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,36 +190,36 @@ namespace dxvk {

VkImageAspectFlags aspectMask = lookupFormatInfo(formatInfo.Format)->aspectMask;

DxvkImageViewCreateInfo viewInfo;
viewInfo.format = formatInfo.Format;
viewInfo.swizzle = formatInfo.Swizzle;
viewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
DxvkImageViewKey viewInfo;
viewInfo.format = formatInfo.Format;
viewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
viewInfo.packedSwizzle = DxvkImageViewKey::packSwizzle(formatInfo.Swizzle);

switch (m_desc.ViewDimension) {
case D3D11_VPIV_DIMENSION_TEXTURE2D:
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.minLevel = m_desc.Texture2D.MipSlice;
viewInfo.numLevels = 1;
viewInfo.minLayer = 0;
viewInfo.numLayers = 1;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.mipIndex = m_desc.Texture2D.MipSlice;
viewInfo.mipCount = 1;
viewInfo.layerIndex = 0;
viewInfo.layerCount = 1;
break;

case D3D11_VPIV_DIMENSION_UNKNOWN:
throw DxvkError("Invalid view dimension");
}

m_subresources.aspectMask = aspectMask;
m_subresources.baseArrayLayer = viewInfo.minLayer;
m_subresources.layerCount = viewInfo.numLayers;
m_subresources.mipLevel = viewInfo.minLevel;
m_subresources.baseArrayLayer = viewInfo.layerIndex;
m_subresources.layerCount = viewInfo.layerCount;
m_subresources.mipLevel = viewInfo.mipIndex;

for (uint32_t i = 0; aspectMask && i < m_views.size(); i++) {
viewInfo.aspect = vk::getNextAspect(aspectMask);
viewInfo.aspects = vk::getNextAspect(aspectMask);

if (viewInfo.aspect != VK_IMAGE_ASPECT_COLOR_BIT)
if (viewInfo.aspects != VK_IMAGE_ASPECT_COLOR_BIT)
viewInfo.format = formatFamily.Formats[i];

m_views[i] = pDevice->GetDXVKDevice()->createImageView(dxvkImage, viewInfo);
m_views[i] = dxvkImage->createView(viewInfo);
}

m_isYCbCr = IsYCbCrFormat(resourceDesc.Format);
Expand Down Expand Up @@ -287,35 +287,34 @@ namespace dxvk {
DXGI_VK_FORMAT_INFO formatInfo = pDevice->LookupFormat(
resourceDesc.Format, DXGI_VK_FORMAT_MODE_COLOR);

DxvkImageViewCreateInfo viewInfo;
viewInfo.format = formatInfo.Format;
viewInfo.aspect = lookupFormatInfo(viewInfo.format)->aspectMask;
viewInfo.swizzle = formatInfo.Swizzle;
viewInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
DxvkImageViewKey viewInfo;
viewInfo.format = formatInfo.Format;
viewInfo.aspects = lookupFormatInfo(viewInfo.format)->aspectMask;
viewInfo.packedSwizzle = DxvkImageViewKey::packSwizzle(formatInfo.Swizzle);
viewInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;

switch (m_desc.ViewDimension) {
case D3D11_VPOV_DIMENSION_TEXTURE2D:
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.minLevel = m_desc.Texture2D.MipSlice;
viewInfo.numLevels = 1;
viewInfo.minLayer = 0;
viewInfo.numLayers = 1;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.mipIndex = m_desc.Texture2D.MipSlice;
viewInfo.mipCount = 1;
viewInfo.layerIndex = 0;
viewInfo.layerCount = 1;
break;

case D3D11_VPOV_DIMENSION_TEXTURE2DARRAY:
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
viewInfo.minLevel = m_desc.Texture2DArray.MipSlice;
viewInfo.numLevels = 1;
viewInfo.minLayer = m_desc.Texture2DArray.FirstArraySlice;
viewInfo.numLayers = m_desc.Texture2DArray.ArraySize;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
viewInfo.mipIndex = m_desc.Texture2DArray.MipSlice;
viewInfo.mipCount = 1;
viewInfo.layerIndex = m_desc.Texture2DArray.FirstArraySlice;
viewInfo.layerCount = m_desc.Texture2DArray.ArraySize;
break;

case D3D11_VPOV_DIMENSION_UNKNOWN:
throw DxvkError("Invalid view dimension");
}

m_view = pDevice->GetDXVKDevice()->createImageView(
GetCommonTexture(pResource)->GetImage(), viewInfo);
m_view = GetCommonTexture(pResource)->GetImage()->createView(viewInfo);
}


Expand Down Expand Up @@ -1262,12 +1261,28 @@ namespace dxvk {
viewport.height = float(cStreamState.dstRect.bottom) - viewport.y;
}

VkExtent3D viewExtent = cViews[0]->mipLevelExtent(0);

VkRect2D srcRect;
srcRect.offset = { 0, 0 };
srcRect.extent = { viewExtent.width, viewExtent.height };

if (cStreamState.srcRectEnabled) {
srcRect.offset.x = cStreamState.srcRect.left;
srcRect.offset.y = cStreamState.srcRect.top;
srcRect.extent.width = cStreamState.srcRect.right - srcRect.offset.x;
srcRect.extent.height = cStreamState.srcRect.bottom - srcRect.offset.y;
}

UboData uboData = { };
uboData.colorMatrix[0][0] = 1.0f;
uboData.colorMatrix[1][1] = 1.0f;
uboData.colorMatrix[2][2] = 1.0f;
uboData.coordMatrix[0][0] = 1.0f;
uboData.coordMatrix[1][1] = 1.0f;
uboData.coordMatrix[0][0] = float(srcRect.extent.width) / float(viewExtent.width);
uboData.coordMatrix[1][1] = float(srcRect.extent.height) / float(viewExtent.height);
uboData.coordMatrix[2][0] = float(srcRect.offset.x) / float(viewExtent.width);
uboData.coordMatrix[2][1] = float(srcRect.offset.y) / float(viewExtent.height);
uboData.srcRect = srcRect;
uboData.yMin = 0.0f;
uboData.yMax = 1.0f;
uboData.isPlanar = cViews[1] != nullptr;
Expand All @@ -1280,27 +1295,24 @@ namespace dxvk {
uboData.yMax = 0.9215686f;
}

DxvkBufferSliceHandle uboSlice = m_ubo->allocSlice();
memcpy(uboSlice.mapPtr, &uboData, sizeof(uboData));
Rc<DxvkResourceAllocation> uboSlice = m_ubo->allocateStorage();
memcpy(uboSlice->mapPtr(), &uboData, sizeof(uboData));

ctx->invalidateBuffer(m_ubo, uboSlice);
ctx->invalidateBuffer(m_ubo, std::move(uboSlice));
ctx->setViewports(1, &viewport, &scissor);

ctx->bindShader<VK_SHADER_STAGE_VERTEX_BIT>(Rc<DxvkShader>(m_vs));
ctx->bindShader<VK_SHADER_STAGE_FRAGMENT_BIT>(Rc<DxvkShader>(m_fs));

ctx->bindUniformBuffer(VK_SHADER_STAGE_FRAGMENT_BIT, 0, DxvkBufferSlice(m_ubo));
ctx->bindResourceSampler(VK_SHADER_STAGE_FRAGMENT_BIT, 1, Rc<DxvkSampler>(m_sampler));

for (uint32_t i = 0; i < cViews.size(); i++)
ctx->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, 2 + i, Rc<DxvkImageView>(cViews[i]));
ctx->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, 1 + i, Rc<DxvkImageView>(cViews[i]));

ctx->draw(3, 1, 0, 0);

ctx->bindResourceSampler(VK_SHADER_STAGE_FRAGMENT_BIT, 1, nullptr);

for (uint32_t i = 0; i < cViews.size(); i++)
ctx->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, 2 + i, nullptr);
ctx->bindResourceImageView(VK_SHADER_STAGE_FRAGMENT_BIT, 1 + i, nullptr);
});
}

Expand All @@ -1315,38 +1327,14 @@ namespace dxvk {
}


void D3D11VideoContext::CreateSampler() {
DxvkSamplerCreateInfo samplerInfo;
samplerInfo.magFilter = VK_FILTER_LINEAR;
samplerInfo.minFilter = VK_FILTER_LINEAR;
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
samplerInfo.mipmapLodBias = 0.0f;
samplerInfo.mipmapLodMin = 0.0f;
samplerInfo.mipmapLodMax = 0.0f;
samplerInfo.useAnisotropy = VK_FALSE;
samplerInfo.maxAnisotropy = 1.0f;
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.compareToDepth = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerInfo.reductionMode = VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE;
samplerInfo.borderColor = VkClearColorValue();
samplerInfo.usePixelCoord = VK_FALSE;
samplerInfo.nonSeamless = VK_FALSE;
m_sampler = m_device->createSampler(samplerInfo);
}


void D3D11VideoContext::CreateShaders() {
SpirvCodeBuffer vsCode(d3d11_video_blit_vert);
SpirvCodeBuffer fsCode(d3d11_video_blit_frag);

const std::array<DxvkBindingInfo, 4> fsBindings = {{
const std::array<DxvkBindingInfo, 3> fsBindings = {{
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, VK_IMAGE_VIEW_TYPE_MAX_ENUM, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_UNIFORM_READ_BIT, VK_TRUE },
{ VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_IMAGE_VIEW_TYPE_MAX_ENUM, VK_SHADER_STAGE_FRAGMENT_BIT, 0 },
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_IMAGE_VIEW_TYPE_2D, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT },
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 2, VK_IMAGE_VIEW_TYPE_2D, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT },
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 3, VK_IMAGE_VIEW_TYPE_2D, VK_SHADER_STAGE_FRAGMENT_BIT, VK_ACCESS_SHADER_READ_BIT },
}};

DxvkShaderCreateInfo vsInfo;
Expand All @@ -1368,7 +1356,6 @@ namespace dxvk {
if (std::exchange(m_resourcesCreated, true))
return;

CreateSampler();
CreateUniformBuffer();
CreateShaders();
}
Expand Down
4 changes: 1 addition & 3 deletions src/d3d11/d3d11_video.h
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,7 @@ namespace dxvk {
struct alignas(16) UboData {
float colorMatrix[3][4];
float coordMatrix[3][2];
VkRect2D srcRect;
float yMin, yMax;
VkBool32 isPlanar;
};
Expand All @@ -593,7 +594,6 @@ namespace dxvk {
Rc<DxvkDevice> m_device;
Rc<DxvkShader> m_vs;
Rc<DxvkShader> m_fs;
Rc<DxvkSampler> m_sampler;
Rc<DxvkBuffer> m_ubo;

VkExtent2D m_dstExtent = { 0u, 0u };
Expand All @@ -613,8 +613,6 @@ namespace dxvk {

void CreateUniformBuffer();

void CreateSampler();

void CreateShaders();

void CreateResources();
Expand Down
90 changes: 47 additions & 43 deletions src/d3d11/d3d11_view_dsv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,58 +17,58 @@ namespace dxvk {
D3D11_COMMON_RESOURCE_DESC resourceDesc;
GetCommonResourceDesc(pResource, &resourceDesc);

DxvkImageViewCreateInfo viewInfo;
DxvkImageViewKey viewInfo;
viewInfo.format = pDevice->LookupFormat(pDesc->Format, DXGI_VK_FORMAT_MODE_DEPTH).Format;
viewInfo.aspect = lookupFormatInfo(viewInfo.format)->aspectMask;
viewInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
viewInfo.aspects = lookupFormatInfo(viewInfo.format)->aspectMask;
viewInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;

switch (pDesc->ViewDimension) {
case D3D11_DSV_DIMENSION_TEXTURE1D:
viewInfo.type = VK_IMAGE_VIEW_TYPE_1D;
viewInfo.minLevel = pDesc->Texture1D.MipSlice;
viewInfo.numLevels = 1;
viewInfo.minLayer = 0;
viewInfo.numLayers = 1;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D;
viewInfo.mipIndex = pDesc->Texture1D.MipSlice;
viewInfo.mipCount = 1;
viewInfo.layerIndex = 0;
viewInfo.layerCount = 1;
break;

case D3D11_DSV_DIMENSION_TEXTURE1DARRAY:
viewInfo.type = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
viewInfo.minLevel = pDesc->Texture1DArray.MipSlice;
viewInfo.numLevels = 1;
viewInfo.minLayer = pDesc->Texture1DArray.FirstArraySlice;
viewInfo.numLayers = pDesc->Texture1DArray.ArraySize;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
viewInfo.mipIndex = pDesc->Texture1DArray.MipSlice;
viewInfo.mipCount = 1;
viewInfo.layerIndex = pDesc->Texture1DArray.FirstArraySlice;
viewInfo.layerCount = pDesc->Texture1DArray.ArraySize;
break;

case D3D11_DSV_DIMENSION_TEXTURE2D:
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.minLevel = pDesc->Texture2D.MipSlice;
viewInfo.numLevels = 1;
viewInfo.minLayer = 0;
viewInfo.numLayers = 1;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.mipIndex = pDesc->Texture2D.MipSlice;
viewInfo.mipCount = 1;
viewInfo.layerIndex = 0;
viewInfo.layerCount = 1;
break;

case D3D11_DSV_DIMENSION_TEXTURE2DARRAY:
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
viewInfo.minLevel = pDesc->Texture2DArray.MipSlice;
viewInfo.numLevels = 1;
viewInfo.minLayer = pDesc->Texture2DArray.FirstArraySlice;
viewInfo.numLayers = pDesc->Texture2DArray.ArraySize;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
viewInfo.mipIndex = pDesc->Texture2DArray.MipSlice;
viewInfo.mipCount = 1;
viewInfo.layerIndex = pDesc->Texture2DArray.FirstArraySlice;
viewInfo.layerCount = pDesc->Texture2DArray.ArraySize;
break;

case D3D11_DSV_DIMENSION_TEXTURE2DMS:
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.minLevel = 0;
viewInfo.numLevels = 1;
viewInfo.minLayer = 0;
viewInfo.numLayers = 1;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.mipIndex = 0;
viewInfo.mipCount = 1;
viewInfo.layerIndex = 0;
viewInfo.layerCount = 1;
break;

case D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY:
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
viewInfo.minLevel = 0;
viewInfo.numLevels = 1;
viewInfo.minLayer = pDesc->Texture2DMSArray.FirstArraySlice;
viewInfo.numLayers = pDesc->Texture2DMSArray.ArraySize;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
viewInfo.mipIndex = 0;
viewInfo.mipCount = 1;
viewInfo.layerIndex = pDesc->Texture2DMSArray.FirstArraySlice;
viewInfo.layerCount = pDesc->Texture2DMSArray.ArraySize;
break;

default:
Expand All @@ -77,20 +77,22 @@ namespace dxvk {

// Normalize view type so that we won't accidentally
// bind 2D array views and 2D views at the same time
if (viewInfo.numLayers == 1) {
if (viewInfo.type == VK_IMAGE_VIEW_TYPE_1D_ARRAY) viewInfo.type = VK_IMAGE_VIEW_TYPE_1D;
if (viewInfo.type == VK_IMAGE_VIEW_TYPE_2D_ARRAY) viewInfo.type = VK_IMAGE_VIEW_TYPE_2D;
if (viewInfo.layerCount == 1) {
if (viewInfo.viewType == VK_IMAGE_VIEW_TYPE_1D_ARRAY)
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D;
if (viewInfo.viewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY)
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
}

// Populate view info struct
m_info.pResource = pResource;
m_info.Dimension = resourceDesc.Dim;
m_info.BindFlags = resourceDesc.BindFlags;
m_info.Image.Aspects = viewInfo.aspect;
m_info.Image.MinLevel = viewInfo.minLevel;
m_info.Image.MinLayer = viewInfo.minLayer;
m_info.Image.NumLevels = viewInfo.numLevels;
m_info.Image.NumLayers = viewInfo.numLayers;
m_info.Image.Aspects = viewInfo.aspects;
m_info.Image.MinLevel = viewInfo.mipIndex;
m_info.Image.MinLayer = viewInfo.layerIndex;
m_info.Image.NumLevels = viewInfo.mipCount;
m_info.Image.NumLayers = viewInfo.layerCount;

if (m_desc.Flags & D3D11_DSV_READ_ONLY_DEPTH)
m_info.Image.Aspects &= ~VK_IMAGE_ASPECT_DEPTH_BIT;
Expand All @@ -99,13 +101,15 @@ namespace dxvk {
m_info.Image.Aspects &= ~VK_IMAGE_ASPECT_STENCIL_BIT;

// Create the underlying image view object
m_view = pDevice->GetDXVKDevice()->createImageView(
GetCommonTexture(pResource)->GetImage(), viewInfo);
m_view = GetCommonTexture(pResource)->GetImage()->createView(viewInfo);
}


D3D11DepthStencilView::~D3D11DepthStencilView() {
ResourceReleasePrivate(m_resource);
m_resource = nullptr;

m_view = nullptr;
}


Expand Down
24 changes: 10 additions & 14 deletions src/d3d11/d3d11_view_dsv.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,24 +50,20 @@ namespace dxvk {
}

VkImageLayout GetRenderLayout() const {
if (m_view->imageInfo().tiling == VK_IMAGE_TILING_OPTIMAL) {
switch (m_desc.Flags & (D3D11_DSV_READ_ONLY_DEPTH | D3D11_DSV_READ_ONLY_STENCIL)) {
default: // case 0
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
case D3D11_DSV_READ_ONLY_DEPTH:
return VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR;
case D3D11_DSV_READ_ONLY_STENCIL:
return VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL_KHR;
case D3D11_DSV_READ_ONLY_DEPTH | D3D11_DSV_READ_ONLY_STENCIL:
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
}
} else {
return VK_IMAGE_LAYOUT_GENERAL;
switch (m_desc.Flags & (D3D11_DSV_READ_ONLY_DEPTH | D3D11_DSV_READ_ONLY_STENCIL)) {
default: // case 0
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
case D3D11_DSV_READ_ONLY_DEPTH:
return VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR;
case D3D11_DSV_READ_ONLY_STENCIL:
return VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL_KHR;
case D3D11_DSV_READ_ONLY_DEPTH | D3D11_DSV_READ_ONLY_STENCIL:
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
}
}

UINT GetSampleCount() const {
return UINT(m_view->imageInfo().sampleCount);
return UINT(m_view->image()->info().sampleCount);
}

VkImageAspectFlags GetWritableAspectMask() const {
Expand Down
105 changes: 55 additions & 50 deletions src/d3d11/d3d11_view_rtv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,100 +22,105 @@ namespace dxvk {
DXGI_VK_FORMAT_INFO formatInfo = pDevice->LookupFormat(
pDesc->Format, DXGI_VK_FORMAT_MODE_COLOR);

DxvkImageViewCreateInfo viewInfo;
viewInfo.format = formatInfo.Format;
viewInfo.aspect = lookupFormatInfo(viewInfo.format)->aspectMask;
viewInfo.swizzle = formatInfo.Swizzle;
viewInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
DxvkImageViewKey viewInfo;
viewInfo.format = formatInfo.Format;
viewInfo.aspects = lookupFormatInfo(viewInfo.format)->aspectMask;
viewInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
viewInfo.packedSwizzle = DxvkImageViewKey::packSwizzle(formatInfo.Swizzle);

switch (pDesc->ViewDimension) {
case D3D11_RTV_DIMENSION_TEXTURE1D:
viewInfo.type = VK_IMAGE_VIEW_TYPE_1D;
viewInfo.minLevel = pDesc->Texture1D.MipSlice;
viewInfo.numLevels = 1;
viewInfo.minLayer = 0;
viewInfo.numLayers = 1;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D;
viewInfo.mipIndex = pDesc->Texture1D.MipSlice;
viewInfo.mipCount = 1;
viewInfo.layerIndex = 0;
viewInfo.layerCount = 1;
break;

case D3D11_RTV_DIMENSION_TEXTURE1DARRAY:
viewInfo.type = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
viewInfo.minLevel = pDesc->Texture1DArray.MipSlice;
viewInfo.numLevels = 1;
viewInfo.minLayer = pDesc->Texture1DArray.FirstArraySlice;
viewInfo.numLayers = pDesc->Texture1DArray.ArraySize;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
viewInfo.mipIndex = pDesc->Texture1DArray.MipSlice;
viewInfo.mipCount = 1;
viewInfo.layerIndex = pDesc->Texture1DArray.FirstArraySlice;
viewInfo.layerCount = pDesc->Texture1DArray.ArraySize;
break;

case D3D11_RTV_DIMENSION_TEXTURE2D:
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.minLevel = pDesc->Texture2D.MipSlice;
viewInfo.numLevels = 1;
viewInfo.minLayer = 0;
viewInfo.numLayers = 1;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.mipIndex = pDesc->Texture2D.MipSlice;
viewInfo.mipCount = 1;
viewInfo.layerIndex = 0;
viewInfo.layerCount = 1;
break;

case D3D11_RTV_DIMENSION_TEXTURE2DARRAY:
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
viewInfo.minLevel = pDesc->Texture2DArray.MipSlice;
viewInfo.numLevels = 1;
viewInfo.minLayer = pDesc->Texture2DArray.FirstArraySlice;
viewInfo.numLayers = pDesc->Texture2DArray.ArraySize;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
viewInfo.mipIndex = pDesc->Texture2DArray.MipSlice;
viewInfo.mipCount = 1;
viewInfo.layerIndex = pDesc->Texture2DArray.FirstArraySlice;
viewInfo.layerCount = pDesc->Texture2DArray.ArraySize;
break;

case D3D11_RTV_DIMENSION_TEXTURE2DMS:
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.minLevel = 0;
viewInfo.numLevels = 1;
viewInfo.minLayer = 0;
viewInfo.numLayers = 1;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.mipIndex = 0;
viewInfo.mipCount = 1;
viewInfo.layerIndex = 0;
viewInfo.layerCount = 1;
break;

case D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY:
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
viewInfo.minLevel = 0;
viewInfo.numLevels = 1;
viewInfo.minLayer = pDesc->Texture2DMSArray.FirstArraySlice;
viewInfo.numLayers = pDesc->Texture2DMSArray.ArraySize;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
viewInfo.mipIndex = 0;
viewInfo.mipCount = 1;
viewInfo.layerIndex = pDesc->Texture2DMSArray.FirstArraySlice;
viewInfo.layerCount = pDesc->Texture2DMSArray.ArraySize;
break;

case D3D11_RTV_DIMENSION_TEXTURE3D:
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
viewInfo.minLevel = pDesc->Texture3D.MipSlice;
viewInfo.numLevels = 1;
viewInfo.minLayer = pDesc->Texture3D.FirstWSlice;
viewInfo.numLayers = pDesc->Texture3D.WSize;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
viewInfo.mipIndex = pDesc->Texture3D.MipSlice;
viewInfo.mipCount = 1;
viewInfo.layerIndex = pDesc->Texture3D.FirstWSlice;
viewInfo.layerCount = pDesc->Texture3D.WSize;
break;

default:
throw DxvkError("D3D11: Invalid view dimension for RTV");
}

if (texture->GetPlaneCount() > 1)
viewInfo.aspect = vk::getPlaneAspect(GetPlaneSlice(pDesc));
viewInfo.aspects = vk::getPlaneAspect(GetPlaneSlice(pDesc));

// Normalize view type so that we won't accidentally
// bind 2D array views and 2D views at the same time
if (viewInfo.numLayers == 1) {
if (viewInfo.type == VK_IMAGE_VIEW_TYPE_1D_ARRAY) viewInfo.type = VK_IMAGE_VIEW_TYPE_1D;
if (viewInfo.type == VK_IMAGE_VIEW_TYPE_2D_ARRAY) viewInfo.type = VK_IMAGE_VIEW_TYPE_2D;
if (viewInfo.layerCount == 1) {
if (viewInfo.viewType == VK_IMAGE_VIEW_TYPE_1D_ARRAY)
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D;
if (viewInfo.viewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY)
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
}

// Populate view info struct
m_info.pResource = pResource;
m_info.Dimension = resourceDesc.Dim;
m_info.BindFlags = resourceDesc.BindFlags;
m_info.Image.Aspects = viewInfo.aspect;
m_info.Image.MinLevel = viewInfo.minLevel;
m_info.Image.MinLayer = viewInfo.minLayer;
m_info.Image.NumLevels = viewInfo.numLevels;
m_info.Image.NumLayers = viewInfo.numLayers;
m_info.Image.Aspects = viewInfo.aspects;
m_info.Image.MinLevel = viewInfo.mipIndex;
m_info.Image.MinLayer = viewInfo.layerIndex;
m_info.Image.NumLevels = viewInfo.mipCount;
m_info.Image.NumLayers = viewInfo.layerCount;

// Create the underlying image view object
m_view = pDevice->GetDXVKDevice()->createImageView(texture->GetImage(), viewInfo);
m_view = texture->GetImage()->createView(viewInfo);
}


D3D11RenderTargetView::~D3D11RenderTargetView() {
ResourceReleasePrivate(m_resource);
m_resource = nullptr;

m_view = nullptr;
}


Expand Down
6 changes: 2 additions & 4 deletions src/d3d11/d3d11_view_rtv.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,11 @@ namespace dxvk {
}

VkImageLayout GetRenderLayout() const {
return m_view->imageInfo().tiling == VK_IMAGE_TILING_OPTIMAL
? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
: VK_IMAGE_LAYOUT_GENERAL;
return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
}

UINT GetSampleCount() const {
return UINT(m_view->imageInfo().sampleCount);
return UINT(m_view->image()->info().sampleCount);
}

D3D10RenderTargetView* GetD3D10Iface() {
Expand Down
148 changes: 76 additions & 72 deletions src/d3d11/d3d11_view_srv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,147 +42,151 @@ namespace dxvk {
}

// Fill in buffer view info
DxvkBufferViewCreateInfo viewInfo;
DxvkBufferViewKey viewInfo;
viewInfo.usage = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;

if (bufInfo.Flags & D3D11_BUFFEREX_SRV_FLAG_RAW) {
// Raw buffer view. We'll represent this as a
// uniform texel buffer with UINT32 elements.
viewInfo.format = VK_FORMAT_R32_UINT;
viewInfo.rangeOffset = sizeof(uint32_t) * bufInfo.FirstElement;
viewInfo.rangeLength = sizeof(uint32_t) * bufInfo.NumElements;
viewInfo.offset = sizeof(uint32_t) * bufInfo.FirstElement;
viewInfo.size = sizeof(uint32_t) * bufInfo.NumElements;
} else if (pDesc->Format == DXGI_FORMAT_UNKNOWN) {
// Structured buffer view
viewInfo.format = VK_FORMAT_R32_UINT;
viewInfo.rangeOffset = buffer->Desc()->StructureByteStride * bufInfo.FirstElement;
viewInfo.rangeLength = buffer->Desc()->StructureByteStride * bufInfo.NumElements;
viewInfo.offset = buffer->Desc()->StructureByteStride * bufInfo.FirstElement;
viewInfo.size = buffer->Desc()->StructureByteStride * bufInfo.NumElements;
} else {
viewInfo.format = pDevice->LookupFormat(pDesc->Format, DXGI_VK_FORMAT_MODE_COLOR).Format;

const DxvkFormatInfo* formatInfo = lookupFormatInfo(viewInfo.format);
viewInfo.rangeOffset = formatInfo->elementSize * bufInfo.FirstElement;
viewInfo.rangeLength = formatInfo->elementSize * bufInfo.NumElements;
viewInfo.offset = formatInfo->elementSize * bufInfo.FirstElement;
viewInfo.size = formatInfo->elementSize * bufInfo.NumElements;
}

// Populate view info struct
m_info.Buffer.Offset = viewInfo.rangeOffset;
m_info.Buffer.Length = viewInfo.rangeLength;
m_info.Buffer.Offset = viewInfo.offset;
m_info.Buffer.Length = viewInfo.size;

// Create underlying buffer view object
m_bufferView = pDevice->GetDXVKDevice()->createBufferView(
buffer->GetBuffer(), viewInfo);
m_bufferView = buffer->GetBuffer()->createView(viewInfo);
} else {
auto texture = GetCommonTexture(pResource);
auto formatInfo = pDevice->LookupFormat(pDesc->Format, texture->GetFormatMode());

DxvkImageViewCreateInfo viewInfo;
viewInfo.format = formatInfo.Format;
viewInfo.aspect = formatInfo.Aspect;
viewInfo.swizzle = formatInfo.Swizzle;
viewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
DxvkImageViewKey viewInfo;
viewInfo.format = formatInfo.Format;
viewInfo.aspects = formatInfo.Aspect;
viewInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT;
viewInfo.packedSwizzle = DxvkImageViewKey::packSwizzle(formatInfo.Swizzle);

// Shaders expect the stencil value in the G component
if (viewInfo.aspect == VK_IMAGE_ASPECT_STENCIL_BIT) {
viewInfo.swizzle = VkComponentMapping {
if (viewInfo.aspects == VK_IMAGE_ASPECT_STENCIL_BIT) {
viewInfo.packedSwizzle = DxvkImageViewKey::packSwizzle({
VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_R,
VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO };
VK_COMPONENT_SWIZZLE_ZERO, VK_COMPONENT_SWIZZLE_ZERO });
}

switch (pDesc->ViewDimension) {
case D3D11_SRV_DIMENSION_TEXTURE1D:
viewInfo.type = VK_IMAGE_VIEW_TYPE_1D;
viewInfo.minLevel = pDesc->Texture1D.MostDetailedMip;
viewInfo.numLevels = pDesc->Texture1D.MipLevels;
viewInfo.minLayer = 0;
viewInfo.numLayers = 1;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D;
viewInfo.mipIndex = pDesc->Texture1D.MostDetailedMip;
viewInfo.mipCount = pDesc->Texture1D.MipLevels;
viewInfo.layerIndex = 0;
viewInfo.layerCount = 1;
break;

case D3D11_SRV_DIMENSION_TEXTURE1DARRAY:
viewInfo.type = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
viewInfo.minLevel = pDesc->Texture1DArray.MostDetailedMip;
viewInfo.numLevels = pDesc->Texture1DArray.MipLevels;
viewInfo.minLayer = pDesc->Texture1DArray.FirstArraySlice;
viewInfo.numLayers = pDesc->Texture1DArray.ArraySize;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
viewInfo.mipIndex = pDesc->Texture1DArray.MostDetailedMip;
viewInfo.mipCount = pDesc->Texture1DArray.MipLevels;
viewInfo.layerIndex = pDesc->Texture1DArray.FirstArraySlice;
viewInfo.layerCount = pDesc->Texture1DArray.ArraySize;
break;

case D3D11_SRV_DIMENSION_TEXTURE2D:
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.minLevel = pDesc->Texture2D.MostDetailedMip;
viewInfo.numLevels = pDesc->Texture2D.MipLevels;
viewInfo.minLayer = 0;
viewInfo.numLayers = 1;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.mipIndex = pDesc->Texture2D.MostDetailedMip;
viewInfo.mipCount = pDesc->Texture2D.MipLevels;
viewInfo.layerIndex = 0;
viewInfo.layerCount = 1;
break;

case D3D11_SRV_DIMENSION_TEXTURE2DARRAY:
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
viewInfo.minLevel = pDesc->Texture2DArray.MostDetailedMip;
viewInfo.numLevels = pDesc->Texture2DArray.MipLevels;
viewInfo.minLayer = pDesc->Texture2DArray.FirstArraySlice;
viewInfo.numLayers = pDesc->Texture2DArray.ArraySize;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
viewInfo.mipIndex = pDesc->Texture2DArray.MostDetailedMip;
viewInfo.mipCount = pDesc->Texture2DArray.MipLevels;
viewInfo.layerIndex = pDesc->Texture2DArray.FirstArraySlice;
viewInfo.layerCount = pDesc->Texture2DArray.ArraySize;
break;

case D3D11_SRV_DIMENSION_TEXTURE2DMS:
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.minLevel = 0;
viewInfo.numLevels = 1;
viewInfo.minLayer = 0;
viewInfo.numLayers = 1;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.mipIndex = 0;
viewInfo.mipCount = 1;
viewInfo.layerIndex = 0;
viewInfo.layerCount = 1;
break;

case D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY:
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
viewInfo.minLevel = 0;
viewInfo.numLevels = 1;
viewInfo.minLayer = pDesc->Texture2DMSArray.FirstArraySlice;
viewInfo.numLayers = pDesc->Texture2DMSArray.ArraySize;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
viewInfo.mipIndex = 0;
viewInfo.mipCount = 1;
viewInfo.layerIndex = pDesc->Texture2DMSArray.FirstArraySlice;
viewInfo.layerCount = pDesc->Texture2DMSArray.ArraySize;
break;

case D3D11_SRV_DIMENSION_TEXTURE3D:
viewInfo.type = VK_IMAGE_VIEW_TYPE_3D;
viewInfo.minLevel = pDesc->Texture3D.MostDetailedMip;
viewInfo.numLevels = pDesc->Texture3D.MipLevels;
viewInfo.minLayer = 0;
viewInfo.numLayers = 1;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_3D;
viewInfo.mipIndex = pDesc->Texture3D.MostDetailedMip;
viewInfo.mipCount = pDesc->Texture3D.MipLevels;
viewInfo.layerIndex = 0;
viewInfo.layerCount = 1;
break;

case D3D11_SRV_DIMENSION_TEXTURECUBE: {
const bool cubeArraysEnabled = pDevice->GetDXVKDevice()->features().core.features.imageCubeArray;
viewInfo.type = cubeArraysEnabled ? VK_IMAGE_VIEW_TYPE_CUBE_ARRAY : VK_IMAGE_VIEW_TYPE_CUBE;
viewInfo.minLevel = pDesc->TextureCube.MostDetailedMip;
viewInfo.numLevels = pDesc->TextureCube.MipLevels;
viewInfo.minLayer = 0;
viewInfo.numLayers = 6;
viewInfo.viewType = cubeArraysEnabled ? VK_IMAGE_VIEW_TYPE_CUBE_ARRAY : VK_IMAGE_VIEW_TYPE_CUBE;
viewInfo.mipIndex = pDesc->TextureCube.MostDetailedMip;
viewInfo.mipCount = pDesc->TextureCube.MipLevels;
viewInfo.layerIndex = 0;
viewInfo.layerCount = 6;
} break;

case D3D11_SRV_DIMENSION_TEXTURECUBEARRAY:
viewInfo.type = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
viewInfo.minLevel = pDesc->TextureCubeArray.MostDetailedMip;
viewInfo.numLevels = pDesc->TextureCubeArray.MipLevels;
viewInfo.minLayer = pDesc->TextureCubeArray.First2DArrayFace;
viewInfo.numLayers = pDesc->TextureCubeArray.NumCubes * 6;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
viewInfo.mipIndex = pDesc->TextureCubeArray.MostDetailedMip;
viewInfo.mipCount = pDesc->TextureCubeArray.MipLevels;
viewInfo.layerIndex = pDesc->TextureCubeArray.First2DArrayFace;
viewInfo.layerCount = pDesc->TextureCubeArray.NumCubes * 6;
break;

default:
throw DxvkError("D3D11: Invalid view dimension for image SRV");
}

if (texture->GetPlaneCount() > 1)
viewInfo.aspect = vk::getPlaneAspect(GetPlaneSlice(pDesc));
viewInfo.aspects = vk::getPlaneAspect(GetPlaneSlice(pDesc));

// Populate view info struct
m_info.Image.Aspects = viewInfo.aspect;
m_info.Image.MinLevel = viewInfo.minLevel;
m_info.Image.MinLayer = viewInfo.minLayer;
m_info.Image.NumLevels = viewInfo.numLevels;
m_info.Image.NumLayers = viewInfo.numLayers;
m_info.Image.Aspects = viewInfo.aspects;
m_info.Image.MinLevel = viewInfo.mipIndex;
m_info.Image.MinLayer = viewInfo.layerIndex;
m_info.Image.NumLevels = viewInfo.mipCount;
m_info.Image.NumLayers = viewInfo.layerCount;

// Create the underlying image view object
m_imageView = pDevice->GetDXVKDevice()->createImageView(texture->GetImage(), viewInfo);
m_imageView = texture->GetImage()->createView(viewInfo);
}
}


D3D11ShaderResourceView::~D3D11ShaderResourceView() {
ResourceReleasePrivate(m_resource);
m_resource = nullptr;

m_imageView = nullptr;
m_bufferView = nullptr;
}


Expand Down Expand Up @@ -298,7 +302,7 @@ namespace dxvk {
D3D11_BUFFER_DESC bufferDesc;
static_cast<D3D11Buffer*>(pResource)->GetDesc(&bufferDesc);

if (bufferDesc.MiscFlags == D3D11_RESOURCE_MISC_BUFFER_STRUCTURED) {
if (bufferDesc.MiscFlags & D3D11_RESOURCE_MISC_BUFFER_STRUCTURED) {
pDesc->Format = DXGI_FORMAT_UNKNOWN;
pDesc->ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
pDesc->Buffer.FirstElement = 0;
Expand Down
117 changes: 61 additions & 56 deletions src/d3d11/d3d11_view_uav.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,110 +25,114 @@ namespace dxvk {
if (resourceDesc.Dim == D3D11_RESOURCE_DIMENSION_BUFFER) {
auto buffer = static_cast<D3D11Buffer*>(pResource);

DxvkBufferViewCreateInfo viewInfo;
DxvkBufferViewKey viewInfo;
viewInfo.usage = VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;

if (pDesc->Buffer.Flags & D3D11_BUFFEREX_SRV_FLAG_RAW) {
viewInfo.format = VK_FORMAT_R32_UINT;
viewInfo.rangeOffset = sizeof(uint32_t) * pDesc->Buffer.FirstElement;
viewInfo.rangeLength = sizeof(uint32_t) * pDesc->Buffer.NumElements;
viewInfo.format = VK_FORMAT_R32_UINT;
viewInfo.offset = sizeof(uint32_t) * pDesc->Buffer.FirstElement;
viewInfo.size = sizeof(uint32_t) * pDesc->Buffer.NumElements;
} else if (pDesc->Format == DXGI_FORMAT_UNKNOWN) {
viewInfo.format = VK_FORMAT_R32_UINT;
viewInfo.rangeOffset = buffer->Desc()->StructureByteStride * pDesc->Buffer.FirstElement;
viewInfo.rangeLength = buffer->Desc()->StructureByteStride * pDesc->Buffer.NumElements;
viewInfo.format = VK_FORMAT_R32_UINT;
viewInfo.offset = buffer->Desc()->StructureByteStride * pDesc->Buffer.FirstElement;
viewInfo.size = buffer->Desc()->StructureByteStride * pDesc->Buffer.NumElements;
} else {
viewInfo.format = pDevice->LookupFormat(pDesc->Format, DXGI_VK_FORMAT_MODE_COLOR).Format;

const DxvkFormatInfo* formatInfo = lookupFormatInfo(viewInfo.format);
viewInfo.rangeOffset = formatInfo->elementSize * pDesc->Buffer.FirstElement;
viewInfo.rangeLength = formatInfo->elementSize * pDesc->Buffer.NumElements;
viewInfo.offset = formatInfo->elementSize * pDesc->Buffer.FirstElement;
viewInfo.size = formatInfo->elementSize * pDesc->Buffer.NumElements;
}

if (pDesc->Buffer.Flags & (D3D11_BUFFER_UAV_FLAG_APPEND | D3D11_BUFFER_UAV_FLAG_COUNTER))
m_counterView = CreateCounterBufferView();

// Populate view info struct
m_info.Buffer.Offset = viewInfo.rangeOffset;
m_info.Buffer.Length = viewInfo.rangeLength;
m_info.Buffer.Offset = viewInfo.offset;
m_info.Buffer.Length = viewInfo.size;

m_bufferView = pDevice->GetDXVKDevice()->createBufferView(
buffer->GetBuffer(), viewInfo);
m_bufferView = buffer->GetBuffer()->createView(viewInfo);
} else {
auto texture = GetCommonTexture(pResource);
auto formatInfo = pDevice->LookupFormat(pDesc->Format, texture->GetFormatMode());

DxvkImageViewCreateInfo viewInfo;
viewInfo.format = formatInfo.Format;
viewInfo.aspect = formatInfo.Aspect;
viewInfo.usage = VK_IMAGE_USAGE_STORAGE_BIT;
DxvkImageViewKey viewInfo;
viewInfo.format = formatInfo.Format;
viewInfo.aspects = formatInfo.Aspect;
viewInfo.usage = VK_IMAGE_USAGE_STORAGE_BIT;

if (!util::isIdentityMapping(formatInfo.Swizzle))
Logger::warn(str::format("UAV format ", pDesc->Format, " has non-identity swizzle, but UAV swizzles are not supported"));

switch (pDesc->ViewDimension) {
case D3D11_UAV_DIMENSION_TEXTURE1D:
viewInfo.type = VK_IMAGE_VIEW_TYPE_1D;
viewInfo.minLevel = pDesc->Texture1D.MipSlice;
viewInfo.numLevels = 1;
viewInfo.minLayer = 0;
viewInfo.numLayers = 1;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D;
viewInfo.mipIndex = pDesc->Texture1D.MipSlice;
viewInfo.mipCount = 1;
viewInfo.layerIndex = 0;
viewInfo.layerCount = 1;
break;

case D3D11_UAV_DIMENSION_TEXTURE1DARRAY:
viewInfo.type = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
viewInfo.minLevel = pDesc->Texture1DArray.MipSlice;
viewInfo.numLevels = 1;
viewInfo.minLayer = pDesc->Texture1DArray.FirstArraySlice;
viewInfo.numLayers = pDesc->Texture1DArray.ArraySize;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
viewInfo.mipIndex = pDesc->Texture1DArray.MipSlice;
viewInfo.mipCount = 1;
viewInfo.layerIndex = pDesc->Texture1DArray.FirstArraySlice;
viewInfo.layerCount = pDesc->Texture1DArray.ArraySize;
break;

case D3D11_UAV_DIMENSION_TEXTURE2D:
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.minLevel = pDesc->Texture2D.MipSlice;
viewInfo.numLevels = 1;
viewInfo.minLayer = 0;
viewInfo.numLayers = 1;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.mipIndex = pDesc->Texture2D.MipSlice;
viewInfo.mipCount = 1;
viewInfo.layerIndex = 0;
viewInfo.layerCount = 1;
break;

case D3D11_UAV_DIMENSION_TEXTURE2DARRAY:
viewInfo.type = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
viewInfo.minLevel = pDesc->Texture2DArray.MipSlice;
viewInfo.numLevels = 1;
viewInfo.minLayer = pDesc->Texture2DArray.FirstArraySlice;
viewInfo.numLayers = pDesc->Texture2DArray.ArraySize;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
viewInfo.mipIndex = pDesc->Texture2DArray.MipSlice;
viewInfo.mipCount = 1;
viewInfo.layerIndex = pDesc->Texture2DArray.FirstArraySlice;
viewInfo.layerCount = pDesc->Texture2DArray.ArraySize;
break;

case D3D11_UAV_DIMENSION_TEXTURE3D:
// FIXME we actually have to map this to a
// 2D array view in order to support W slices
viewInfo.type = VK_IMAGE_VIEW_TYPE_3D;
viewInfo.minLevel = pDesc->Texture3D.MipSlice;
viewInfo.numLevels = 1;
viewInfo.minLayer = 0;
viewInfo.numLayers = 1;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_3D;
viewInfo.mipIndex = pDesc->Texture3D.MipSlice;
viewInfo.mipCount = 1;
viewInfo.layerIndex = 0;
viewInfo.layerCount = 1;
break;

default:
throw DxvkError("D3D11: Invalid view dimension for image UAV");
}

if (texture->GetPlaneCount() > 1)
viewInfo.aspect = vk::getPlaneAspect(GetPlaneSlice(pDesc));
viewInfo.aspects = vk::getPlaneAspect(GetPlaneSlice(pDesc));

// Populate view info struct
m_info.Image.Aspects = viewInfo.aspect;
m_info.Image.MinLevel = viewInfo.minLevel;
m_info.Image.MinLayer = viewInfo.minLayer;
m_info.Image.NumLevels = viewInfo.numLevels;
m_info.Image.NumLayers = viewInfo.numLayers;

m_imageView = pDevice->GetDXVKDevice()->createImageView(
GetCommonTexture(pResource)->GetImage(), viewInfo);
m_info.Image.Aspects = viewInfo.aspects;
m_info.Image.MinLevel = viewInfo.mipIndex;
m_info.Image.MinLayer = viewInfo.layerIndex;
m_info.Image.NumLevels = viewInfo.mipCount;
m_info.Image.NumLayers = viewInfo.layerCount;

m_imageView = GetCommonTexture(pResource)->GetImage()->createView(viewInfo);
}
}


D3D11UnorderedAccessView::~D3D11UnorderedAccessView() {
ResourceReleasePrivate(m_resource);
m_resource = nullptr;

m_bufferView = nullptr;
m_counterView = nullptr;
m_imageView = nullptr;
}


Expand Down Expand Up @@ -214,7 +218,7 @@ namespace dxvk {
D3D11_BUFFER_DESC bufferDesc;
static_cast<D3D11Buffer*>(pResource)->GetDesc(&bufferDesc);

if (bufferDesc.MiscFlags == D3D11_RESOURCE_MISC_BUFFER_STRUCTURED) {
if (bufferDesc.MiscFlags & D3D11_RESOURCE_MISC_BUFFER_STRUCTURED) {
pDesc->Format = DXGI_FORMAT_UNKNOWN;
pDesc->ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
pDesc->Buffer.FirstElement = 0;
Expand Down Expand Up @@ -449,12 +453,13 @@ namespace dxvk {

Rc<DxvkBuffer> buffer = device->createBuffer(info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);

DxvkBufferViewCreateInfo viewInfo;
DxvkBufferViewKey viewInfo;
viewInfo.format = VK_FORMAT_UNDEFINED;
viewInfo.rangeOffset = 0;
viewInfo.rangeLength = sizeof(uint32_t);
viewInfo.offset = 0;
viewInfo.size = sizeof(uint32_t);
viewInfo.usage = VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;

return device->createBufferView(buffer, viewInfo);
return buffer->createView(viewInfo);
}

}
11 changes: 9 additions & 2 deletions src/d3d11/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,25 @@ else
d3d11_dxgi_dep = dxgi_dep
endif

d3d11_dll = shared_library('d3d11'+dll_ext, dxgi_common_src + d3d11_src + d3d10_src,
d3d11_dll = shared_library(dxvk_name_prefix+'d3d11', dxgi_common_src + d3d11_src + d3d10_src,
glsl_generator.process(d3d11_shaders), d3d11_res,
name_prefix : dxvk_name_prefix,
dependencies : [ d3d11_dxgi_dep, dxbc_dep, dxvk_dep ],
include_directories : dxvk_include_path,
install : true,
vs_module_defs : 'd3d11'+def_spec_ext,
link_args : d3d11_ld_args,
link_depends : [ d3d11_link_depends ],
kwargs : dxvk_so_version,
)

d3d11_dep = declare_dependency(
link_with : [ d3d11_dll ],
include_directories : [ dxvk_include_path ],
)

if platform != 'windows'
pkg.generate(d3d11_dll,
filebase: dxvk_pkg_prefix + 'd3d11',
subdirs: 'dxvk',
)
endif
77 changes: 58 additions & 19 deletions src/d3d11/shaders/d3d11_video_blit_frag.frag
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#version 450

#extension GL_EXT_samplerless_texture_functions : require

// Can't use matrix types here since even a two-row
// matrix will be padded to 16 bytes per column for
// absolutely no reason
Expand All @@ -11,6 +13,8 @@ uniform ubo_t {
vec2 coord_matrix_c1;
vec2 coord_matrix_c2;
vec2 coord_matrix_c3;
uvec2 src_offset;
uvec2 src_extent;
float y_min;
float y_max;
bool is_planar;
Expand All @@ -19,9 +23,8 @@ uniform ubo_t {
layout(location = 0) in vec2 i_texcoord;
layout(location = 0) out vec4 o_color;

layout(set = 0, binding = 1) uniform sampler s_sampler;
layout(set = 0, binding = 2) uniform texture2D s_inputY;
layout(set = 0, binding = 3) uniform texture2D s_inputCbCr;
layout(set = 0, binding = 1) uniform texture2D s_inputY;
layout(set = 0, binding = 2) uniform texture2D s_inputCbCr;

void main() {
// Transform input texture coordinates to
Expand All @@ -31,25 +34,61 @@ void main() {
coord_matrix_c2,
coord_matrix_c3);

vec2 coord = coord_matrix * vec3(i_texcoord, 1.0f);

// Fetch source image color
vec4 color = vec4(0.0f, 0.0f, 0.0f, 1.0f);

if (is_planar) {
color.g = texture(sampler2D(s_inputY, s_sampler), coord).r;
color.rb = texture(sampler2D(s_inputCbCr, s_sampler), coord).gr;
color.g = clamp((color.g - y_min) / (y_max - y_min), 0.0f, 1.0f);
} else {
color = texture(sampler2D(s_inputY, s_sampler), coord);
}

// Color space transformation
// Load color space transform
mat3x4 color_matrix = mat3x4(
color_matrix_r1,
color_matrix_r2,
color_matrix_r3);

o_color.rgb = vec4(color.rgb, 1.0f) * color_matrix;
o_color.a = color.a;
// Compute actual pixel coordinates to sample. We filter
// manually in order to avoid bleeding from pixels outside
// the source rectangle.
vec2 abs_size_y = vec2(textureSize(s_inputY, 0));
vec2 abs_size_c = vec2(textureSize(s_inputCbCr, 0));

vec2 coord = coord_matrix * vec3(i_texcoord, 1.0f);
coord -= 0.5f / abs_size_y;

vec2 size_factor = abs_size_c / abs_size_y;

vec2 src_lo = vec2(src_offset);
vec2 src_hi = vec2(src_offset + src_extent - 1u);

vec2 abs_coord = coord * abs_size_y;
vec2 fract_coord = fract(clamp(abs_coord, src_lo, src_hi));

vec4 accum = vec4(0.0f, 0.0f, 0.0f, 0.0f);

for (int i = 0; i < 4; i++) {
ivec2 offset = ivec2(i & 1, i >> 1);

// Compute exact pixel coordinates for the current
// iteration and clamp it to the source rectangle.
vec2 fetch_coord = clamp(abs_coord + vec2(offset), src_lo, src_hi);

// Fetch actual pixel color in source color space
vec4 color;

if (is_planar) {
color.g = texelFetch(s_inputY, ivec2(fetch_coord), 0).r;
color.rb = texelFetch(s_inputCbCr, ivec2(fetch_coord * size_factor), 0).gr;
color.g = clamp((color.g - y_min) / (y_max - y_min), 0.0f, 1.0f);
color.a = 1.0f;
} else {
color = texelFetch(s_inputY, ivec2(fetch_coord), 0);
}

// Transform color space before accumulation
color.rgb = vec4(color.rgb, 1.0f) * color_matrix;

// Filter and accumulate final pixel color
vec2 factor = fract_coord;

if (offset.x == 0) factor.x = 1.0f - factor.x;
if (offset.y == 0) factor.y = 1.0f - factor.y;

accum += factor.x * factor.y * color;
}

o_color = accum;
}
6 changes: 6 additions & 0 deletions src/d3d8/d3d8.def
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
LIBRARY D3D8.DLL
EXPORTS
ValidatePixelShader @ 2
ValidateVertexShader @ 3
DebugSetMute @ 4
Direct3DCreate8 @ 5
10 changes: 10 additions & 0 deletions src/d3d8/d3d8.sym
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
global:
ValidatePixelShader;
ValidateVertexShader;
DebugSetMute;
Direct3DCreate8;

local:
*;
};
243 changes: 243 additions & 0 deletions src/d3d8/d3d8_batch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
#pragma once

#include "d3d8_include.h"
#include "d3d8_buffer.h"
#include "d3d8_format.h"

#include <vector>
#include <cstdint>
#include <climits>

namespace dxvk {

inline constexpr size_t D3DPT_COUNT = size_t(D3DPT_TRIANGLEFAN) + 1;
inline constexpr D3DPRIMITIVETYPE D3DPT_INVALID = D3DPRIMITIVETYPE(0);

// Vertex buffer that can handle many tiny locks while
// still maintaing the lock ordering of direct-mapped buffers.
class D3D8BatchBuffer final : public D3D8VertexBuffer {
public:
D3D8BatchBuffer(
D3D8Device* pDevice,
D3DPOOL Pool,
DWORD Usage,
UINT Length,
DWORD FVF)
: D3D8VertexBuffer(pDevice, nullptr, Pool, Usage)
, m_data(Length)
, m_fvf(FVF) {
}

HRESULT STDMETHODCALLTYPE Lock(
UINT OffsetToLock,
UINT SizeToLock,
BYTE** ppbData,
DWORD Flags) {
*ppbData = m_data.data() + OffsetToLock;
return D3D_OK;
}

HRESULT STDMETHODCALLTYPE Unlock() {
return D3D_OK;
}

HRESULT STDMETHODCALLTYPE GetDesc(D3DVERTEXBUFFER_DESC* pDesc) {
if (unlikely(pDesc == nullptr))
return D3DERR_INVALIDCALL;

pDesc->Format = D3DFMT_VERTEXDATA;
pDesc->Type = D3DRTYPE_VERTEXBUFFER;
pDesc->Usage = m_usage;
pDesc->Pool = m_pool;
pDesc->Size = m_data.size();
pDesc->FVF = m_fvf;

return D3D_OK;
}

void STDMETHODCALLTYPE PreLoad() {
}

const void* GetPtr(UINT byteOffset = 0) const {
return m_data.data() + byteOffset;
}

UINT Size() const {
return m_data.size();
}

private:
std::vector<BYTE> m_data;
DWORD m_fvf;
};


// Main handler for batching D3D8 draw calls.
class D3D8Batcher {

struct Batch {
D3DPRIMITIVETYPE PrimitiveType = D3DPT_INVALID;
std::vector<uint16_t> Indices;
UINT Offset = 0;
UINT MinVertex = UINT_MAX;
UINT MaxVertex = 0;
UINT PrimitiveCount = 0;
UINT DrawCallCount = 0;
};

public:
D3D8Batcher(D3D8Device* pDevice8, Com<d3d9::IDirect3DDevice9>&& pDevice9)
: m_device8(pDevice8)
, m_device(std::move(pDevice9)) {
}

inline D3D8BatchBuffer* CreateVertexBuffer(UINT Length, DWORD Usage, DWORD FVF, D3DPOOL Pool) {
return ref(new D3D8BatchBuffer(m_device8, Pool, Usage, Length, FVF));
}

inline void StateChange() {
if (likely(m_batches.empty()))
return;
for (auto& draw : m_batches) {

if (draw.PrimitiveType == D3DPT_INVALID)
continue;

for (auto& index : draw.Indices)
index -= draw.MinVertex;

m_device->DrawIndexedPrimitiveUP(
d3d9::D3DPRIMITIVETYPE(draw.PrimitiveType),
0,
draw.MaxVertex - draw.MinVertex,
draw.PrimitiveCount,
draw.Indices.data(),
d3d9::D3DFMT_INDEX16,
m_stream->GetPtr(draw.MinVertex * m_stride),
m_stride);

m_device->SetStreamSource(0, D3D8VertexBuffer::GetD3D9Nullable(m_stream), 0, m_stride);
m_device->SetIndices(D3D8IndexBuffer::GetD3D9Nullable(m_indices));

draw.PrimitiveType = D3DPRIMITIVETYPE(0);
draw.Offset = 0;
draw.MinVertex = UINT_MAX;
draw.MaxVertex = 0;
draw.PrimitiveCount = 0;
draw.DrawCallCount = 0;
}
}

inline void EndFrame() {
// Nothing to be done.
}

inline HRESULT DrawPrimitive(
D3DPRIMITIVETYPE PrimitiveType,
UINT StartVertex,
UINT PrimitiveCount) {

// None of this linestrip or fan malarkey
D3DPRIMITIVETYPE batchedPrimType = PrimitiveType;
switch (PrimitiveType) {
case D3DPT_LINESTRIP: batchedPrimType = D3DPT_LINELIST; break;
case D3DPT_TRIANGLEFAN: batchedPrimType = D3DPT_TRIANGLELIST; break;
default: break;
}

Batch* batch = &m_batches[size_t(batchedPrimType)];
batch->PrimitiveType = batchedPrimType;

//UINT vertices = GetVertexCount8(PrimitiveType, PrimitiveCount);

switch (PrimitiveType) {
case D3DPT_POINTLIST:
batch->Indices.resize(batch->Offset + PrimitiveCount);
for (UINT i = 0; i < PrimitiveCount; i++)
batch->Indices[batch->Offset++] = (StartVertex + i);
break;
case D3DPT_LINELIST:
batch->Indices.resize(batch->Offset + PrimitiveCount * 2);
for (UINT i = 0; i < PrimitiveCount; i++) {
batch->Indices[batch->Offset++] = (StartVertex + i * 2 + 0);
batch->Indices[batch->Offset++] = (StartVertex + i * 2 + 1);
}
break;
case D3DPT_LINESTRIP:
batch->Indices.resize(batch->Offset + PrimitiveCount * 2);
for (UINT i = 0; i < PrimitiveCount; i++) {
batch->Indices[batch->Offset++] = (StartVertex + i + 0);
batch->Indices[batch->Offset++] = (StartVertex + i + 1);
}
break;
case D3DPT_TRIANGLELIST:
batch->Indices.resize(batch->Offset + PrimitiveCount * 3);
for (UINT i = 0; i < PrimitiveCount; i++) {
batch->Indices[batch->Offset++] = (StartVertex + i * 3 + 0);
batch->Indices[batch->Offset++] = (StartVertex + i * 3 + 1);
batch->Indices[batch->Offset++] = (StartVertex + i * 3 + 2);
}
break;
case D3DPT_TRIANGLESTRIP:
// Join with degenerate triangle
// 1 2 3, 3 4, 4 5 6
batch->Indices.resize(batch->Offset + PrimitiveCount + 2);
if (batch->Offset > 0) {
batch->Indices[batch->Offset + 1] = batch->Indices[batch->Offset-2];
batch->Indices[batch->Offset += 2] = StartVertex;
}
for (UINT i = 0; i < PrimitiveCount; i++) {
batch->Indices[batch->Offset++] = (StartVertex + i + 0);
}
break;
// 1 2 3 4 5 6 7 -> 1 2 3, 1 3 4, 1 4 5, 1 5 6, 1 6 7
case D3DPT_TRIANGLEFAN:
batch->Indices.resize(batch->Offset + PrimitiveCount * 3);
for (UINT i = 0; i < PrimitiveCount; i++) {
batch->Indices[batch->Offset++] = (StartVertex + 0);
batch->Indices[batch->Offset++] = (StartVertex + i + 1);
batch->Indices[batch->Offset++] = (StartVertex + i + 2);
}
break;
default:
return D3DERR_INVALIDCALL;
}
batch->MinVertex = std::min(batch->MinVertex, StartVertex);
if (!batch->Indices.empty())
batch->MaxVertex = std::max(batch->MaxVertex, UINT(batch->Indices.back() + 1));
batch->PrimitiveCount += PrimitiveCount;
batch->DrawCallCount++;
return D3D_OK;
}

inline void SetStream(UINT num, D3D8VertexBuffer* stream, UINT stride) {
if (unlikely(num != 0)) {
StateChange();
return;
}
if (unlikely(m_stream != stream || m_stride != stride)) {
StateChange();
m_stream = static_cast<D3D8BatchBuffer*>(stream);
m_stride = stride;
}
}

inline void SetIndices(D3D8IndexBuffer* indices, INT baseVertexIndex) {
if (m_indices != indices || m_baseVertexIndex != baseVertexIndex) {
StateChange();
m_indices = indices;
m_baseVertexIndex = baseVertexIndex;
}
}

private:
D3D8Device* m_device8;
Com<d3d9::IDirect3DDevice9> m_device;

D3D8BatchBuffer* m_stream = nullptr;
UINT m_stride = 0;
D3D8IndexBuffer* m_indices = nullptr;
INT m_baseVertexIndex = 0;
std::array<Batch, D3DPT_COUNT> m_batches;
};
}
99 changes: 99 additions & 0 deletions src/d3d8/d3d8_buffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#pragma once

#include "d3d8_include.h"
#include "d3d8_resource.h"

namespace dxvk {

template <typename D3D9, typename D3D8>
class D3D8Buffer : public D3D8Resource<D3D9, D3D8> {

public:

D3D8Buffer(
D3D8Device* pDevice,
Com<D3D9>&& pBuffer,
D3DPOOL Pool,
DWORD Usage)
: D3D8Resource<D3D9, D3D8> (pDevice, std::move(pBuffer))
, m_pool (Pool)
, m_usage (Usage) {
m_options = this->GetParent()->GetOptions();
}

HRESULT STDMETHODCALLTYPE Lock(
UINT OffsetToLock,
UINT SizeToLock,
BYTE** ppbData,
DWORD Flags) {

if (m_options->forceLegacyDiscard &&
(Flags & D3DLOCK_DISCARD) &&
!((m_usage & D3DUSAGE_DYNAMIC) &&
(m_usage & D3DUSAGE_WRITEONLY)))
Flags &= ~D3DLOCK_DISCARD;

return this->GetD3D9()->Lock(
OffsetToLock,
SizeToLock,
reinterpret_cast<void**>(ppbData),
Flags);
}

HRESULT STDMETHODCALLTYPE Unlock() {
return this->GetD3D9()->Unlock();
}

void STDMETHODCALLTYPE PreLoad() {
this->GetD3D9()->PreLoad();
}

protected:
const D3D8Options* m_options;
const D3DPOOL m_pool;
const DWORD m_usage;
};


using D3D8VertexBufferBase = D3D8Buffer<d3d9::IDirect3DVertexBuffer9, IDirect3DVertexBuffer8>;
class D3D8VertexBuffer : public D3D8VertexBufferBase {

public:

D3D8VertexBuffer(
D3D8Device* pDevice,
Com<d3d9::IDirect3DVertexBuffer9>&& pBuffer,
D3DPOOL Pool,
DWORD Usage)
: D3D8VertexBufferBase(pDevice, std::move(pBuffer), Pool, Usage) {
}

D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_VERTEXBUFFER; }

HRESULT STDMETHODCALLTYPE GetDesc(D3DVERTEXBUFFER_DESC* pDesc) {
return GetD3D9()->GetDesc(reinterpret_cast<d3d9::D3DVERTEXBUFFER_DESC*>(pDesc));
}

};

using D3D8IndexBufferBase = D3D8Buffer<d3d9::IDirect3DIndexBuffer9, IDirect3DIndexBuffer8>;
class D3D8IndexBuffer final : public D3D8IndexBufferBase {

public:

D3D8IndexBuffer(
D3D8Device* pDevice,
Com<d3d9::IDirect3DIndexBuffer9>&& pBuffer,
D3DPOOL Pool,
DWORD Usage)
: D3D8IndexBufferBase(pDevice, std::move(pBuffer), Pool, Usage) {
}

D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final { return D3DRTYPE_INDEXBUFFER; }

HRESULT STDMETHODCALLTYPE GetDesc(D3DINDEXBUFFER_DESC* pDesc) final {
return GetD3D9()->GetDesc(reinterpret_cast<d3d9::D3DINDEXBUFFER_DESC*>(pDesc));
}

};
}
8 changes: 8 additions & 0 deletions src/d3d8/d3d8_caps.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#pragma once

namespace dxvk::d8caps {

inline constexpr uint32_t MAX_TEXTURE_STAGES = 8;
inline constexpr uint32_t MAX_STREAMS = 16;

}
180 changes: 180 additions & 0 deletions src/d3d8/d3d8_d3d9_util.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
#pragma once

// Utility functions for converting
// between DirectX8 and DirectX9 types.

#include "d3d8_include.h"
#include "d3d8_format.h"
#include "d3d8_options.h"

#include <utility>

namespace dxvk {

// (8<-9) D3DCAPSX: Writes to D3DCAPS8 from D3DCAPS9
inline void ConvertCaps8(const d3d9::D3DCAPS9& caps9, D3DCAPS8* pCaps8) {

// should be aligned
std::memcpy(pCaps8, &caps9, sizeof(D3DCAPS8));

// Max supported shader model is PS 1.4 and VS 1.1
pCaps8->VertexShaderVersion = D3DVS_VERSION(1, 1);
pCaps8->PixelShaderVersion = D3DPS_VERSION(1, 4);

// This was removed by D3D9. We can probably render windowed.
pCaps8->Caps2 |= D3DCAPS2_CANRENDERWINDOWED;

// Replaced by D3DPRASTERCAPS_DEPTHBIAS in D3D9
pCaps8->RasterCaps |= D3DPRASTERCAPS_ZBIAS;


// Remove D3D9-specific caps:
pCaps8->Caps2 &= ~D3DCAPS2_CANAUTOGENMIPMAP;

pCaps8->Caps3 &= ~D3DCAPS3_LINEAR_TO_SRGB_PRESENTATION
& ~D3DCAPS3_COPY_TO_VIDMEM
& ~D3DCAPS3_COPY_TO_SYSTEMMEM;

pCaps8->PrimitiveMiscCaps &= ~D3DPMISCCAPS_INDEPENDENTWRITEMASKS
& ~D3DPMISCCAPS_PERSTAGECONSTANT
& ~D3DPMISCCAPS_FOGANDSPECULARALPHA
& ~D3DPMISCCAPS_SEPARATEALPHABLEND
& ~D3DPMISCCAPS_MRTINDEPENDENTBITDEPTHS
& ~D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING
& ~D3DPMISCCAPS_FOGVERTEXCLAMPED
& ~D3DPMISCCAPS_POSTBLENDSRGBCONVERT;

pCaps8->RasterCaps &= ~D3DPRASTERCAPS_SCISSORTEST
& ~D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS
& ~D3DPRASTERCAPS_DEPTHBIAS
& ~D3DPRASTERCAPS_MULTISAMPLE_TOGGLE;

pCaps8->SrcBlendCaps &= ~D3DPBLENDCAPS_BLENDFACTOR
& ~D3DPBLENDCAPS_INVSRCCOLOR2
& ~D3DPBLENDCAPS_SRCCOLOR2;

pCaps8->DestBlendCaps &= ~D3DPBLENDCAPS_BLENDFACTOR
& ~D3DPBLENDCAPS_INVSRCCOLOR2
& ~D3DPBLENDCAPS_SRCCOLOR2;

pCaps8->LineCaps &= ~D3DLINECAPS_ANTIALIAS;

pCaps8->StencilCaps &= ~D3DSTENCILCAPS_TWOSIDED;

pCaps8->VertexProcessingCaps &= ~D3DVTXPCAPS_TEXGEN_SPHEREMAP;
}

// (9<-8) D3DD3DPRESENT_PARAMETERS: Returns D3D9's params given an input for D3D8
inline d3d9::D3DPRESENT_PARAMETERS ConvertPresentParameters9(D3DPRESENT_PARAMETERS* pParams) {
// A 0 back buffer count needs to be corrected and made visible to the D3D8 application as well
pParams->BackBufferCount = std::max(pParams->BackBufferCount, 1u);

if (pParams->BackBufferFormat == D3DFMT_UNKNOWN)
pParams->BackBufferFormat = D3DFMT_X8R8G8B8;

d3d9::D3DPRESENT_PARAMETERS params;
params.BackBufferWidth = pParams->BackBufferWidth;
params.BackBufferHeight = pParams->BackBufferHeight;
params.BackBufferFormat = d3d9::D3DFORMAT(pParams->BackBufferFormat);
params.BackBufferCount = pParams->BackBufferCount;

params.MultiSampleType = d3d9::D3DMULTISAMPLE_TYPE(pParams->MultiSampleType);
params.MultiSampleQuality = 0; // (D3D8: no MultiSampleQuality), TODO: get a value for this

UINT PresentationInterval = pParams->FullScreen_PresentationInterval;

if (pParams->Windowed) {

if (unlikely(PresentationInterval != D3DPRESENT_INTERVAL_DEFAULT)) {
// TODO: what does dx8 do if windowed app sets FullScreen_PresentationInterval?
Logger::warn(str::format(
"D3D8: Application is windowed yet requested FullScreen_PresentationInterval ", PresentationInterval,
" (should be D3DPRESENT_INTERVAL_DEFAULT). This will be ignored."));
}

// D3D8: For windowed swap chain, the back buffer is copied to the window immediately.
PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
}

D3DSWAPEFFECT SwapEffect = pParams->SwapEffect;

// D3DSWAPEFFECT_COPY_VSYNC has been removed
if (SwapEffect == D3DSWAPEFFECT_COPY_VSYNC) {

SwapEffect = D3DSWAPEFFECT_COPY;

// D3D8: In windowed mode, D3DSWAPEFFECT_COPY_VSYNC enables VSYNC.
// In fullscreen, D3DPRESENT_INTERVAL_IMMEDIATE is meaningless.
if (pParams->Windowed || (PresentationInterval & D3DPRESENT_INTERVAL_IMMEDIATE) != 0) {
PresentationInterval = D3DPRESENT_INTERVAL_ONE;
// TODO: what does dx8 do if multiple D3DPRESENT_INTERVAL flags are set?
}
}

params.SwapEffect = d3d9::D3DSWAPEFFECT(SwapEffect);
params.hDeviceWindow = pParams->hDeviceWindow;
params.Windowed = pParams->Windowed;
params.EnableAutoDepthStencil = pParams->EnableAutoDepthStencil;
params.AutoDepthStencilFormat = d3d9::D3DFORMAT(pParams->AutoDepthStencilFormat);
params.Flags = pParams->Flags;

// D3DPRESENT_RATE_UNLIMITED is unsupported, use D3DPRESENT_RATE_DEFAULT (or 0)
if (unlikely(pParams->FullScreen_RefreshRateInHz == D3DPRESENT_RATE_UNLIMITED)) {
params.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
} else {
params.FullScreen_RefreshRateInHz = pParams->FullScreen_RefreshRateInHz;
}

// FullScreen_PresentationInterval -> PresentationInterval
params.PresentationInterval = PresentationInterval;

return params;
}

// (8<-9) Convert D3DSURFACE_DESC
inline void ConvertSurfaceDesc8(const d3d9::D3DSURFACE_DESC* pSurf9, D3DSURFACE_DESC* pSurf8) {
pSurf8->Format = D3DFORMAT(pSurf9->Format);
pSurf8->Type = D3DRESOURCETYPE(pSurf9->Type);
pSurf8->Usage = pSurf9->Usage;
pSurf8->Pool = D3DPOOL(pSurf9->Pool);
pSurf8->Size = getSurfaceSize(pSurf8->Format, pSurf9->Width, pSurf9->Height);

pSurf8->MultiSampleType = D3DMULTISAMPLE_TYPE(pSurf9->MultiSampleType);
// DX8: No multisample quality
pSurf8->Width = pSurf9->Width;
pSurf8->Height = pSurf9->Height;
}

// (8<-9) Convert D3DVOLUME_DESC
inline void ConvertVolumeDesc8(const d3d9::D3DVOLUME_DESC* pVol9, D3DVOLUME_DESC* pVol8) {
pVol8->Format = D3DFORMAT(pVol9->Format);
pVol8->Type = D3DRESOURCETYPE(pVol9->Type);
pVol8->Usage = pVol9->Usage;
pVol8->Pool = D3DPOOL(pVol9->Pool);
pVol8->Size = getSurfaceSize(pVol8->Format, pVol9->Width, pVol9->Height) * pVol9->Depth;
pVol8->Width = pVol9->Width;
pVol8->Height = pVol9->Height;
pVol8->Depth = pVol9->Depth;
}

// If this D3DTEXTURESTAGESTATETYPE has been remapped to a d3d9::D3DSAMPLERSTATETYPE
// it will be returned, otherwise returns -1u
inline d3d9::D3DSAMPLERSTATETYPE GetSamplerStateType9(const D3DTEXTURESTAGESTATETYPE StageType) {
switch (StageType) {
// 13-21:
case D3DTSS_ADDRESSU: return d3d9::D3DSAMP_ADDRESSU;
case D3DTSS_ADDRESSV: return d3d9::D3DSAMP_ADDRESSV;
case D3DTSS_BORDERCOLOR: return d3d9::D3DSAMP_BORDERCOLOR;
case D3DTSS_MAGFILTER: return d3d9::D3DSAMP_MAGFILTER;
case D3DTSS_MINFILTER: return d3d9::D3DSAMP_MINFILTER;
case D3DTSS_MIPFILTER: return d3d9::D3DSAMP_MIPFILTER;
case D3DTSS_MIPMAPLODBIAS: return d3d9::D3DSAMP_MIPMAPLODBIAS;
case D3DTSS_MAXMIPLEVEL: return d3d9::D3DSAMP_MAXMIPLEVEL;
case D3DTSS_MAXANISOTROPY: return d3d9::D3DSAMP_MAXANISOTROPY;
// 25:
case D3DTSS_ADDRESSW: return d3d9::D3DSAMP_ADDRESSW;
default: return d3d9::D3DSAMPLERSTATETYPE(-1u);
}
}
}

2,020 changes: 2,020 additions & 0 deletions src/d3d8/d3d8_device.cpp

Large diffs are not rendered by default.

469 changes: 469 additions & 0 deletions src/d3d8/d3d8_device.h

Large diffs are not rendered by default.

71 changes: 71 additions & 0 deletions src/d3d8/d3d8_device_child.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#pragma once

// Common methods for device-tied objects.
// - AddRef, Release from IUnknown
// - GetDevice from various classes including IDirect3DResource8

#include "d3d8_include.h"
#include "d3d8_wrapped_object.h"

namespace dxvk {

class D3D8Device;

template <typename D3D9, typename D3D8>
class D3D8DeviceChild : public D3D8WrappedObject<D3D9, D3D8> {

public:

D3D8DeviceChild(D3D8Device* pDevice, Com<D3D9>&& Object)
: D3D8WrappedObject<D3D9, D3D8>(std::move(Object))
, m_parent( pDevice ) { }

ULONG STDMETHODCALLTYPE AddRef() {
uint32_t refCount = this->m_refCount++;
if (unlikely(!refCount)) {
this->AddRefPrivate();
GetDevice()->AddRef();
}

return refCount + 1;
}

ULONG STDMETHODCALLTYPE Release() {
// ignore Release calls on objects with 0 refCount
if(unlikely(!this->m_refCount))
return this->m_refCount;

uint32_t refCount = --this->m_refCount;
if (unlikely(!refCount)) {
auto* pDevice = GetDevice();
this->ReleasePrivate();
pDevice->Release();
}
return refCount;
}

HRESULT STDMETHODCALLTYPE GetDevice(IDirect3DDevice8** ppDevice) {
InitReturnPtr(ppDevice);

if (ppDevice == nullptr)
return D3DERR_INVALIDCALL;

*ppDevice = ref(GetDevice());
return D3D_OK;
}

IDirect3DDevice8* GetDevice() {
return reinterpret_cast<IDirect3DDevice8*>(m_parent);
}

D3D8Device* GetParent() {
return m_parent;
}

protected:

D3D8Device* m_parent;

};

}
220 changes: 220 additions & 0 deletions src/d3d8/d3d8_format.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
#pragma once

#include "d3d8_include.h"

namespace dxvk {
constexpr bool isDXT(D3DFORMAT fmt) {
return fmt == D3DFMT_DXT1
|| fmt == D3DFMT_DXT2
|| fmt == D3DFMT_DXT3
|| fmt == D3DFMT_DXT4
|| fmt == D3DFMT_DXT5;
}

constexpr bool isDXT(d3d9::D3DFORMAT fmt) {
return isDXT(D3DFORMAT(fmt));
}

constexpr bool isUnsupportedSurfaceFormat(D3DFORMAT fmt) {
// mirror what dxvk doesn't support in terms of d3d9 surface formats
return fmt == D3DFMT_R8G8B8
|| fmt == D3DFMT_R3G3B2
|| fmt == D3DFMT_A8R3G3B2
|| fmt == D3DFMT_A8P8
|| fmt == D3DFMT_P8;
// not included in the d3d8 spec
//|| fmt == D3DFMT_CXV8U8;
}

constexpr bool isSupportedDepthStencilFormat(D3DFORMAT fmt) {
// native d3d8 doesn't support D3DFMT_D32, D3DFMT_D15S1 or D3DFMT_D24X4S4
return fmt == D3DFMT_D16_LOCKABLE
|| fmt == D3DFMT_D16
//|| fmt == D3DFMT_D32
//|| fmt == D3DFMT_D15S1
//|| fmt == D3DFMT_D24X4S4
|| fmt == D3DFMT_D24S8
|| fmt == D3DFMT_D24X8;
}

constexpr bool isDepthStencilFormat(D3DFORMAT fmt) {
return fmt == D3DFMT_D16_LOCKABLE
|| fmt == D3DFMT_D16
|| fmt == D3DFMT_D32
|| fmt == D3DFMT_D15S1
|| fmt == D3DFMT_D24X4S4
|| fmt == D3DFMT_D24S8
|| fmt == D3DFMT_D24X8;
}

// Get bytes per pixel (or 4x4 block for DXT)
constexpr UINT getFormatStride(D3DFORMAT fmt) {
switch (fmt) {
default:
case D3DFMT_UNKNOWN:
return 0;
case D3DFMT_R3G3B2:
case D3DFMT_A8:
case D3DFMT_P8:
case D3DFMT_L8:
case D3DFMT_A4L4:
return 1;
case D3DFMT_R5G6B5:
case D3DFMT_X1R5G5B5:
case D3DFMT_A1R5G5B5:
case D3DFMT_A4R4G4B4:
case D3DFMT_A8R3G3B2:
case D3DFMT_X4R4G4B4:
case D3DFMT_A8P8:
case D3DFMT_A8L8:
case D3DFMT_V8U8:
case D3DFMT_L6V5U5:
case D3DFMT_D16_LOCKABLE:
case D3DFMT_D15S1:
case D3DFMT_D16:
case D3DFMT_UYVY:
case D3DFMT_YUY2:
return 2;
case D3DFMT_R8G8B8:
return 3;
case D3DFMT_A8R8G8B8:
case D3DFMT_X8R8G8B8:
case D3DFMT_A2B10G10R10:
//case D3DFMT_A8B8G8R8:
//case D3DFMT_X8B8G8R8:
case D3DFMT_G16R16:
case D3DFMT_X8L8V8U8:
case D3DFMT_Q8W8V8U8:
case D3DFMT_V16U16:
case D3DFMT_W11V11U10:
case D3DFMT_A2W10V10U10:
case D3DFMT_D32:
case D3DFMT_D24S8:
case D3DFMT_D24X8:
case D3DFMT_D24X4S4:
return 4;
case D3DFMT_DXT1:
return 8;
case D3DFMT_DXT2:
case D3DFMT_DXT3:
case D3DFMT_DXT4:
case D3DFMT_DXT5:
return 16;
}
}

constexpr uint32_t GetVertexCount8(D3DPRIMITIVETYPE type, UINT count) {
switch (type) {
default:
case D3DPT_TRIANGLELIST: return count * 3;
case D3DPT_POINTLIST: return count;
case D3DPT_LINELIST: return count * 2;
case D3DPT_LINESTRIP: return count + 1;
case D3DPT_TRIANGLESTRIP: return count + 2;
case D3DPT_TRIANGLEFAN: return count + 2;
}
}

// Essentially the same logic as D3D9VertexDecl::SetFVF
constexpr UINT GetFVFStride(DWORD FVF) {
uint32_t texCount = 0;

uint32_t betas = 0;
uint8_t betaIdx = 0xFF;

UINT size = 0;

switch (FVF & D3DFVF_POSITION_MASK) {
case D3DFVF_XYZ:
case D3DFVF_XYZB1:
case D3DFVF_XYZB2:
case D3DFVF_XYZB3:
case D3DFVF_XYZB4:
case D3DFVF_XYZB5:
size += sizeof(float) * 3;

if ((FVF & D3DFVF_POSITION_MASK) == D3DFVF_XYZ)
break;

betas = (((FVF & D3DFVF_XYZB5) - D3DFVF_XYZB1) >> 1) + 1;
if (FVF & D3DFVF_LASTBETA_D3DCOLOR)
betaIdx = sizeof(D3DCOLOR);
else if (FVF & D3DFVF_LASTBETA_UBYTE4)
betaIdx = sizeof(BYTE) * 4;
else if ((FVF & D3DFVF_XYZB5) == D3DFVF_XYZB5)
betaIdx = sizeof(float);

if (betaIdx != 0xFF)
betas--;

if (betas > 0) {
if (betas <= 4)
size += sizeof(float) * betas;
}

if (betaIdx != 0xFF) {
size += betaIdx;
}
break;

case D3DFVF_XYZW:
case D3DFVF_XYZRHW:
size += sizeof(float) * 4;
break;

default:
break;
}

if (FVF & D3DFVF_NORMAL) {
size += sizeof(float) * 3;
}
if (FVF & D3DFVF_PSIZE) {
size += sizeof(float);
}
if (FVF & D3DFVF_DIFFUSE) {
size += sizeof(D3DCOLOR);
}
if (FVF & D3DFVF_SPECULAR) {
size += sizeof(D3DCOLOR);
}

texCount = (FVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
texCount = std::min(texCount, 8u);

for (uint32_t i = 0; i < texCount; i++) {
switch ((FVF >> (16 + i * 2)) & 0x3) {
case D3DFVF_TEXTUREFORMAT1:
size += sizeof(float);
break;

case D3DFVF_TEXTUREFORMAT2:
size += sizeof(float) * 2;
break;

case D3DFVF_TEXTUREFORMAT3:
size += sizeof(float) * 3;
break;

case D3DFVF_TEXTUREFORMAT4:
size += sizeof(float) * 4;
break;

default:
break;
}
}

return size;
}


constexpr UINT getSurfaceSize(D3DFORMAT Format, UINT Width, UINT Height) {
if (isDXT(Format)) {
Width = ((Width + 3) >> 2);
Height = ((Height + 3) >> 2);
}
return Width * Height * getFormatStride(Format);
}

}
206 changes: 206 additions & 0 deletions src/d3d8/d3d8_include.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
#pragma once

#ifndef _MSC_VER
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0A00
#endif

#include <stdint.h>
#include <d3d8.h>

// Declare __uuidof for D3D8 interfaces
#ifdef __CRT_UUID_DECL
__CRT_UUID_DECL(IDirect3D8, 0x1DD9E8DA,0x1C77,0x4D40,0xB0,0xCF,0x98,0xFE,0xFD,0xFF,0x95,0x12);
__CRT_UUID_DECL(IDirect3DDevice8, 0x7385E5DF,0x8FE8,0x41D5,0x86,0xB6,0xD7,0xB4,0x85,0x47,0xB6,0xCF);
__CRT_UUID_DECL(IDirect3DResource8, 0x1B36BB7B,0x09B7,0x410A,0xB4,0x45,0x7D,0x14,0x30,0xD7,0xB3,0x3F);
__CRT_UUID_DECL(IDirect3DVertexBuffer8, 0x8AEEEAC7,0x05F9,0x44D4,0xB5,0x91,0x00,0x0B,0x0D,0xF1,0xCB,0x95);
__CRT_UUID_DECL(IDirect3DVolume8, 0xBD7349F5,0x14F1,0x42E4,0x9C,0x79,0x97,0x23,0x80,0xDB,0x40,0xC0);
__CRT_UUID_DECL(IDirect3DSwapChain8, 0x928C088B,0x76B9,0x4C6B,0xA5,0x36,0xA5,0x90,0x85,0x38,0x76,0xCD);
__CRT_UUID_DECL(IDirect3DSurface8, 0xB96EEBCA,0xB326,0x4EA5,0x88,0x2F,0x2F,0xF5,0xBA,0xE0,0x21,0xDD);
__CRT_UUID_DECL(IDirect3DIndexBuffer8, 0x0E689C9A,0x053D,0x44A0,0x9D,0x92,0xDB,0x0E,0x3D,0x75,0x0F,0x86);
__CRT_UUID_DECL(IDirect3DBaseTexture8, 0xB4211CFA,0x51B9,0x4A9F,0xAB,0x78,0xDB,0x99,0xB2,0xBB,0x67,0x8E);
__CRT_UUID_DECL(IDirect3DTexture8, 0xE4CDD575,0x2866,0x4F01,0xB1,0x2E,0x7E,0xEC,0xE1,0xEC,0x93,0x58);
__CRT_UUID_DECL(IDirect3DCubeTexture8, 0x3EE5B968,0x2ACA,0x4C34,0x8B,0xB5,0x7E,0x0C,0x3D,0x19,0xB7,0x50);
__CRT_UUID_DECL(IDirect3DVolumeTexture8, 0x4B8AAAFA,0x140F,0x42BA,0x91,0x31,0x59,0x7E,0xAF,0xAA,0x2E,0xAD);
#elif defined(_MSC_VER)
interface DECLSPEC_UUID("1DD9E8DA-1C77-4D40-B0CF-98FEFDFF9512") IDirect3D8;
interface DECLSPEC_UUID("7385E5DF-8FE8-41D5-86B6-D7B48547B6CF") IDirect3DDevice8;
interface DECLSPEC_UUID("1B36BB7B-09B7-410A-B445-7D1430D7B33F") IDirect3DResource8;
interface DECLSPEC_UUID("8AEEEAC7-05F9-44D4-B591-000B0DF1CB95") IDirect3DVertexBuffer8;
interface DECLSPEC_UUID("BD7349F5-14F1-42E4-9C79-972380DB40C0") IDirect3DVolume8;
interface DECLSPEC_UUID("928C088B-76B9-4C6B-A536-A590853876CD") IDirect3DSwapChain8;
interface DECLSPEC_UUID("B96EEBCA-B326-4EA5-882F-2FF5BAE021DD") IDirect3DSurface8;
interface DECLSPEC_UUID("0E689C9A-053D-44A0-9D92-DB0E3D750F86") IDirect3DIndexBuffer8;
interface DECLSPEC_UUID("B4211CFA-51B9-4A9F-AB78-DB99B2BB678E") IDirect3DBaseTexture8;
interface DECLSPEC_UUID("E4CDD575-2866-4F01-B12E-7EECE1EC9358") IDirect3DTexture8;
interface DECLSPEC_UUID("3EE5B968-2ACA-4C34-8BB5-7E0C3D19B750") IDirect3DCubeTexture8;
interface DECLSPEC_UUID("4B8AAAFA-140F-42BA-9131-597EAFAA2EAD") IDirect3DVolumeTexture8;
#endif

// Undefine D3D8 macros
#undef DIRECT3D_VERSION
#undef D3D_SDK_VERSION

#undef D3DCS_ALL // parentheses added in D3D9
#undef D3DFVF_POSITION_MASK // changed from 0x00E to 0x400E in D3D9
#undef D3DFVF_RESERVED2 // reduced from 4 to 2 in DX9

#undef D3DSP_REGNUM_MASK // changed from 0x00000FFF to 0x000007FF in D3D9


#if defined(__MINGW32__) || defined(__GNUC__)

// Avoid redundant definitions (add D3D*_DEFINED macros here)
#define D3DRECT_DEFINED
#define D3DMATRIX_DEFINED

// Temporarily override __CRT_UUID_DECL to allow usage in d3d9 namespace
#pragma push_macro("__CRT_UUID_DECL")
#ifdef __CRT_UUID_DECL
#undef __CRT_UUID_DECL
#endif

#ifdef __MINGW32__
#define __CRT_UUID_DECL(type,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \
} \
extern "C++" template<> struct __mingw_uuidof_s<d3d9::type> { static constexpr IID __uuid_inst = {l,w1,w2, {b1,b2,b3,b4,b5,b6,b7,b8}}; }; \
extern "C++" template<> constexpr const GUID &__mingw_uuidof<d3d9::type>() { return __mingw_uuidof_s<d3d9::type>::__uuid_inst; } \
extern "C++" template<> constexpr const GUID &__mingw_uuidof<d3d9::type*>() { return __mingw_uuidof_s<d3d9::type>::__uuid_inst; } \
namespace d3d9 {

#elif defined(__GNUC__)
#define __CRT_UUID_DECL(type, a, b, c, d, e, f, g, h, i, j, k) \
} \
extern "C++" { template <> constexpr GUID __uuidof_helper<d3d9::type>() { return GUID{a,b,c,{d,e,f,g,h,i,j,k}}; } } \
extern "C++" { template <> constexpr GUID __uuidof_helper<d3d9::type*>() { return __uuidof_helper<d3d9::type>(); } } \
extern "C++" { template <> constexpr GUID __uuidof_helper<const d3d9::type*>() { return __uuidof_helper<d3d9::type>(); } } \
extern "C++" { template <> constexpr GUID __uuidof_helper<d3d9::type&>() { return __uuidof_helper<d3d9::type>(); } } \
extern "C++" { template <> constexpr GUID __uuidof_helper<const d3d9::type&>() { return __uuidof_helper<d3d9::type>(); } } \
namespace d3d9 {
#endif

#endif // defined(__MINGW32__) || defined(__GNUC__)


/**
* \brief Direct3D 9
*
* All D3D9 interfaces are included within
* a namespace, so as not to collide with
* D3D8 interfaces.
*/
namespace d3d9 {
#include <d3d9.h>
}

// Indicates d3d9:: namespace is in-use.
#define DXVK_D3D9_NAMESPACE

#if defined(__MINGW32__) || defined(__GNUC__)
#pragma pop_macro("__CRT_UUID_DECL")
#endif

//for some reason we need to specify __declspec(dllexport) for MinGW
#if defined(__WINE__) || !defined(_WIN32)
#define DLLEXPORT __attribute__((visibility("default")))
#else
#define DLLEXPORT
#endif


#include "../util/com/com_guid.h"
#include "../util/com/com_object.h"
#include "../util/com/com_pointer.h"

#include "../util/log/log.h"
#include "../util/log/log_debug.h"

#include "../util/sync/sync_recursive.h"

#include "../util/util_error.h"
#include "../util/util_likely.h"
#include "../util/util_string.h"

// Missed definitions in Wine/MinGW.

#ifndef D3DPRESENT_BACK_BUFFERS_MAX_EX
#define D3DPRESENT_BACK_BUFFERS_MAX_EX 30
#endif

#ifndef D3DSI_OPCODE_MASK
#define D3DSI_OPCODE_MASK 0x0000FFFF
#endif

#ifndef D3DSP_TEXTURETYPE_MASK
#define D3DSP_TEXTURETYPE_MASK 0x78000000
#endif

#ifndef D3DUSAGE_AUTOGENMIPMAP
#define D3DUSAGE_AUTOGENMIPMAP 0x00000400L
#endif

#ifndef D3DSP_DCL_USAGE_MASK
#define D3DSP_DCL_USAGE_MASK 0x0000000f
#endif

#ifndef D3DSP_OPCODESPECIFICCONTROL_MASK
#define D3DSP_OPCODESPECIFICCONTROL_MASK 0x00ff0000
#endif

#ifndef D3DSP_OPCODESPECIFICCONTROL_SHIFT
#define D3DSP_OPCODESPECIFICCONTROL_SHIFT 16
#endif

#ifndef D3DCURSOR_IMMEDIATE_UPDATE
#define D3DCURSOR_IMMEDIATE_UPDATE 0x00000001L
#endif

#ifndef D3DPRESENT_FORCEIMMEDIATE
#define D3DPRESENT_FORCEIMMEDIATE 0x00000100L
#endif

// From d3dtypes.h

#ifndef D3DDEVINFOID_TEXTUREMANAGER
#define D3DDEVINFOID_TEXTUREMANAGER 1
#endif

#ifndef D3DDEVINFOID_D3DTEXTUREMANAGER
#define D3DDEVINFOID_D3DTEXTUREMANAGER 2
#endif

#ifndef D3DDEVINFOID_TEXTURING
#define D3DDEVINFOID_TEXTURING 3
#endif

// From d3dhal.h

#ifndef D3DDEVINFOID_VCACHE
#define D3DDEVINFOID_VCACHE 4
#endif

// MinGW headers are broken. Who'dve guessed?
#ifndef _MSC_VER

// Missing from d3d8types.h
#ifndef D3DDEVINFOID_RESOURCEMANAGER
#define D3DDEVINFOID_RESOURCEMANAGER 5
#endif

#ifndef D3DDEVINFOID_VERTEXSTATS
#define D3DDEVINFOID_VERTEXSTATS 6 // Aka D3DDEVINFOID_D3DVERTEXSTATS
#endif

#ifndef D3DPRESENT_RATE_UNLIMITED
#define D3DPRESENT_RATE_UNLIMITED 0x7FFFFFFF
#endif

#else // _MSC_VER

// These are enum typedefs in the MinGW headers, but not defined by Microsoft
#define D3DVSDT_TYPE DWORD
#define D3DVSDE_REGISTER DWORD

#endif
153 changes: 153 additions & 0 deletions src/d3d8/d3d8_interface.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#include "d3d8_interface.h"

#include "d3d8_device.h"
#include "d3d8_texture.h"

#include <cstring>

namespace dxvk
{
D3D8Interface::D3D8Interface() {
m_d3d9 = d3d9::Direct3DCreate9(D3D_SDK_VERSION);

// Get the bridge interface to D3D9.
if (FAILED(m_d3d9->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), (void**)&m_bridge))) {
throw DxvkError("D3D8Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!");
}

m_bridge->SetD3D8CompatibilityMode(true);

m_d3d8Options = D3D8Options(*m_bridge->GetConfig());

m_adapterCount = m_d3d9->GetAdapterCount();
m_adapterModeCounts.resize(m_adapterCount);
m_adapterModes.reserve(m_adapterCount);

for (UINT adapter = 0; adapter < m_adapterCount; adapter++) {
m_adapterModes.emplace_back();

// cache adapter modes and mode counts for each d3d9 format
for (d3d9::D3DFORMAT fmt : ADAPTER_FORMATS) {

const UINT modeCount = m_d3d9->GetAdapterModeCount(adapter, fmt);
for (UINT mode = 0; mode < modeCount; mode++) {

m_adapterModes[adapter].emplace_back();
m_d3d9->EnumAdapterModes(adapter, fmt, mode, &(m_adapterModes[adapter].back()));

// can't use modeCount as it's only for one fmt
m_adapterModeCounts[adapter]++;
}
}
}
}

HRESULT STDMETHODCALLTYPE D3D8Interface::QueryInterface(REFIID riid, void** ppvObject) {
if (ppvObject == nullptr)
return E_POINTER;

*ppvObject = nullptr;

if (riid == __uuidof(IUnknown)
|| riid == __uuidof(IDirect3D8)) {
*ppvObject = ref(this);
return S_OK;
}

Logger::warn("D3D8Interface::QueryInterface: Unknown interface query");
Logger::warn(str::format(riid));
return E_NOINTERFACE;
}

HRESULT STDMETHODCALLTYPE D3D8Interface::GetAdapterIdentifier(
UINT Adapter,
DWORD Flags,
D3DADAPTER_IDENTIFIER8* pIdentifier) {
if (unlikely(pIdentifier == nullptr))
return D3DERR_INVALIDCALL;

// This flag now has the opposite effect.
// Either way, WHQLevel will be 1 with Direct3D9Ex
if (Flags & D3DENUM_NO_WHQL_LEVEL)
Flags &= ~D3DENUM_WHQL_LEVEL;
else
Flags |= D3DENUM_WHQL_LEVEL;

d3d9::D3DADAPTER_IDENTIFIER9 identifier9;
HRESULT res = m_d3d9->GetAdapterIdentifier(Adapter, Flags, &identifier9);

if (likely(SUCCEEDED(res))) {
strncpy(pIdentifier->Driver, identifier9.Driver, MAX_DEVICE_IDENTIFIER_STRING);
strncpy(pIdentifier->Description, identifier9.Description, MAX_DEVICE_IDENTIFIER_STRING);

pIdentifier->DriverVersion = identifier9.DriverVersion;
pIdentifier->VendorId = identifier9.VendorId;
pIdentifier->DeviceId = identifier9.DeviceId;
pIdentifier->SubSysId = identifier9.SubSysId;
pIdentifier->Revision = identifier9.Revision;
pIdentifier->DeviceIdentifier = identifier9.DeviceIdentifier;

pIdentifier->WHQLLevel = identifier9.WHQLLevel;
}

return res;
}

HRESULT __stdcall D3D8Interface::EnumAdapterModes(
UINT Adapter,
UINT Mode,
D3DDISPLAYMODE* pMode) {
if (Adapter >= m_adapterCount || Mode >= m_adapterModeCounts[Adapter] || pMode == nullptr) {
return D3DERR_INVALIDCALL;
}

pMode->Width = m_adapterModes[Adapter][Mode].Width;
pMode->Height = m_adapterModes[Adapter][Mode].Height;
pMode->RefreshRate = m_adapterModes[Adapter][Mode].RefreshRate;
pMode->Format = D3DFORMAT(m_adapterModes[Adapter][Mode].Format);

return D3D_OK;
}

HRESULT __stdcall D3D8Interface::CreateDevice(
UINT Adapter,
D3DDEVTYPE DeviceType,
HWND hFocusWindow,
DWORD BehaviorFlags,
D3DPRESENT_PARAMETERS* pPresentationParameters,
IDirect3DDevice8** ppReturnedDeviceInterface) {
InitReturnPtr(ppReturnedDeviceInterface);

if (unlikely(pPresentationParameters == nullptr ||
ppReturnedDeviceInterface == nullptr))
return D3DERR_INVALIDCALL;

// D3DSWAPEFFECT_COPY can not be used with more than one back buffer.
// This is also technically true for D3DSWAPEFFECT_COPY_VSYNC, however
// RC Cars depends on it not being rejected.
if (unlikely(pPresentationParameters->SwapEffect == D3DSWAPEFFECT_COPY
&& pPresentationParameters->BackBufferCount > 1))
return D3DERR_INVALIDCALL;

Com<d3d9::IDirect3DDevice9> pDevice9 = nullptr;
d3d9::D3DPRESENT_PARAMETERS params = ConvertPresentParameters9(pPresentationParameters);
HRESULT res = m_d3d9->CreateDevice(
Adapter,
(d3d9::D3DDEVTYPE)DeviceType,
hFocusWindow,
BehaviorFlags,
&params,
&pDevice9
);

if (likely(SUCCEEDED(res)))
*ppReturnedDeviceInterface = ref(new D3D8Device(
this, std::move(pDevice9),
DeviceType, hFocusWindow, BehaviorFlags,
pPresentationParameters
));

return res;
}

}
172 changes: 172 additions & 0 deletions src/d3d8/d3d8_interface.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
#pragma once

#include "d3d8_include.h"
#include "d3d8_d3d9_util.h"
#include "d3d8_options.h"
#include "d3d8_format.h"
#include "../d3d9/d3d9_bridge.h"

namespace dxvk {

/**
* \brief D3D8 interface implementation
*
* Implements the IDirect3DDevice8 interfaces
* which provides the way to get adapters and create other objects such as \ref IDirect3DDevice8.
* similar to \ref DxgiFactory but for D3D8.
*/
class D3D8Interface final : public ComObjectClamp<IDirect3D8> {

static constexpr d3d9::D3DFORMAT ADAPTER_FORMATS[] = {
d3d9::D3DFMT_A1R5G5B5,
//d3d9::D3DFMT_A2R10G10B10, (not in D3D8)
d3d9::D3DFMT_A8R8G8B8,
d3d9::D3DFMT_R5G6B5,
d3d9::D3DFMT_X1R5G5B5,
d3d9::D3DFMT_X8R8G8B8
};

public:
D3D8Interface();

HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject);

HRESULT STDMETHODCALLTYPE RegisterSoftwareDevice(void* pInitializeFunction) {
return m_d3d9->RegisterSoftwareDevice(pInitializeFunction);
}

UINT STDMETHODCALLTYPE GetAdapterCount() {
return m_d3d9->GetAdapterCount();
}

HRESULT STDMETHODCALLTYPE GetAdapterIdentifier(
UINT Adapter,
DWORD Flags,
D3DADAPTER_IDENTIFIER8* pIdentifier);

UINT STDMETHODCALLTYPE GetAdapterModeCount(UINT Adapter) {
return m_adapterModeCounts[Adapter];
}

HRESULT STDMETHODCALLTYPE EnumAdapterModes(
UINT Adapter,
UINT Mode,
D3DDISPLAYMODE* pMode);

HRESULT STDMETHODCALLTYPE GetAdapterDisplayMode(UINT Adapter, D3DDISPLAYMODE* pMode) {
return m_d3d9->GetAdapterDisplayMode(Adapter, (d3d9::D3DDISPLAYMODE*)pMode);
}

HRESULT STDMETHODCALLTYPE CheckDeviceType(
UINT Adapter,
D3DDEVTYPE DevType,
D3DFORMAT AdapterFormat,
D3DFORMAT BackBufferFormat,
BOOL bWindowed) {
// Ignore the bWindowed parameter when querying D3D9. D3D8 does
// identical validations between windowed and fullscreen modes, adhering
// to the stricter fullscreen adapter and back buffer format validations.
return m_d3d9->CheckDeviceType(
Adapter,
(d3d9::D3DDEVTYPE)DevType,
(d3d9::D3DFORMAT)AdapterFormat,
(d3d9::D3DFORMAT)BackBufferFormat,
FALSE
);
}

HRESULT STDMETHODCALLTYPE CheckDeviceFormat(
UINT Adapter,
D3DDEVTYPE DeviceType,
D3DFORMAT AdapterFormat,
DWORD Usage,
D3DRESOURCETYPE RType,
D3DFORMAT CheckFormat) {
return m_d3d9->CheckDeviceFormat(
Adapter,
(d3d9::D3DDEVTYPE)DeviceType,
(d3d9::D3DFORMAT)AdapterFormat,
Usage,
(d3d9::D3DRESOURCETYPE)RType,
(d3d9::D3DFORMAT)CheckFormat
);
}

HRESULT STDMETHODCALLTYPE CheckDeviceMultiSampleType(
UINT Adapter,
D3DDEVTYPE DeviceType,
D3DFORMAT SurfaceFormat,
BOOL Windowed,
D3DMULTISAMPLE_TYPE MultiSampleType) {
DWORD* pQualityLevels = nullptr;
return m_d3d9->CheckDeviceMultiSampleType(
Adapter,
(d3d9::D3DDEVTYPE)DeviceType,
(d3d9::D3DFORMAT)SurfaceFormat,
Windowed,
(d3d9::D3DMULTISAMPLE_TYPE)MultiSampleType,
pQualityLevels
);
}

HRESULT STDMETHODCALLTYPE CheckDepthStencilMatch(
UINT Adapter,
D3DDEVTYPE DeviceType,
D3DFORMAT AdapterFormat,
D3DFORMAT RenderTargetFormat,
D3DFORMAT DepthStencilFormat) {
if (isSupportedDepthStencilFormat(DepthStencilFormat))
return m_d3d9->CheckDepthStencilMatch(
Adapter,
(d3d9::D3DDEVTYPE)DeviceType,
(d3d9::D3DFORMAT)AdapterFormat,
(d3d9::D3DFORMAT)RenderTargetFormat,
(d3d9::D3DFORMAT)DepthStencilFormat
);

return D3DERR_NOTAVAILABLE;
}

HRESULT STDMETHODCALLTYPE GetDeviceCaps(
UINT Adapter,
D3DDEVTYPE DeviceType,
D3DCAPS8* pCaps) {
if (unlikely(pCaps == nullptr))
return D3DERR_INVALIDCALL;

d3d9::D3DCAPS9 caps9;
HRESULT res = m_d3d9->GetDeviceCaps(Adapter, (d3d9::D3DDEVTYPE)DeviceType, &caps9);

if (likely(SUCCEEDED(res)))
dxvk::ConvertCaps8(caps9, pCaps);

return res;
}

HMONITOR STDMETHODCALLTYPE GetAdapterMonitor(UINT Adapter) {
return m_d3d9->GetAdapterMonitor(Adapter);
}

HRESULT STDMETHODCALLTYPE CreateDevice(
UINT Adapter,
D3DDEVTYPE DeviceType,
HWND hFocusWindow,
DWORD BehaviorFlags,
D3DPRESENT_PARAMETERS* pPresentationParameters,
IDirect3DDevice8** ppReturnedDeviceInterface);


const D3D8Options& GetOptions() { return m_d3d8Options; }

private:

UINT m_adapterCount;
std::vector<UINT> m_adapterModeCounts;
std::vector<std::vector<d3d9::D3DDISPLAYMODE>> m_adapterModes;

Com<d3d9::IDirect3D9> m_d3d9;
Com<IDxvkD3D8InterfaceBridge> m_bridge;
D3D8Options m_d3d8Options;
};

}
65 changes: 65 additions & 0 deletions src/d3d8/d3d8_main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include "d3d8_interface.h"

namespace dxvk {
Logger Logger::s_instance("d3d8.log");

HRESULT CreateD3D8(IDirect3D8** ppDirect3D8) {
if (!ppDirect3D8)
return D3DERR_INVALIDCALL;

*ppDirect3D8 = ref(new D3D8Interface());
return D3D_OK;
}
}

extern "C" {

DLLEXPORT HRESULT __stdcall ValidatePixelShader(
const DWORD* pPixelShader,
const D3DCAPS8* pCaps,
BOOL errorReturn,
char** pErrorString) {
dxvk::Logger::warn("D3D8: ValidatePixelShader: Stub");

if (unlikely(pPixelShader == nullptr))
return E_FAIL;

if (errorReturn && pErrorString != nullptr) {
const char* errorMessage = "";
*pErrorString = (char *) errorMessage;
}

return S_OK;
}

DLLEXPORT HRESULT __stdcall ValidateVertexShader(
const DWORD* pVertexShader,
const DWORD* pVertexDecl,
const D3DCAPS8* pCaps,
BOOL errorReturn,
char** pErrorString) {
dxvk::Logger::warn("D3D8: ValidateVertexShader: Stub");

if (unlikely(pVertexShader == nullptr))
return E_FAIL;

if (errorReturn && pErrorString != nullptr) {
const char* errorMessage = "";
*pErrorString = (char *) errorMessage;
}

return S_OK;
}

DLLEXPORT void __stdcall DebugSetMute() {
dxvk::Logger::debug("D3D8: DebugSetMute: Stub");
}

DLLEXPORT IDirect3D8* __stdcall Direct3DCreate8(UINT nSDKVersion) {
IDirect3D8* pDirect3D = nullptr;
dxvk::CreateD3D8(&pDirect3D);

return pDirect3D;
}

}
9 changes: 9 additions & 0 deletions src/d3d8/d3d8_multithread.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include "d3d8_device.h"

namespace dxvk {

D3D8Multithread::D3D8Multithread(
BOOL Protected)
: m_protected( Protected ) { }

}
77 changes: 77 additions & 0 deletions src/d3d8/d3d8_multithread.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#pragma once

#include "d3d8_include.h"

namespace dxvk {

/**
* \brief Device lock
*
* Lightweight RAII wrapper that implements
* a subset of the functionality provided by
* \c std::unique_lock, with the goal of being
* cheaper to construct and destroy.
*/
class D3D8DeviceLock {

public:

D3D8DeviceLock()
: m_mutex(nullptr) { }

D3D8DeviceLock(sync::RecursiveSpinlock& mutex)
: m_mutex(&mutex) {
mutex.lock();
}

D3D8DeviceLock(D3D8DeviceLock&& other)
: m_mutex(other.m_mutex) {
other.m_mutex = nullptr;
}

D3D8DeviceLock& operator = (D3D8DeviceLock&& other) {
if (m_mutex)
m_mutex->unlock();

m_mutex = other.m_mutex;
other.m_mutex = nullptr;
return *this;
}

~D3D8DeviceLock() {
if (m_mutex != nullptr)
m_mutex->unlock();
}

private:

sync::RecursiveSpinlock* m_mutex;

};


/**
* \brief D3D8 context lock
*/
class D3D8Multithread {

public:

D3D8Multithread(
BOOL Protected);

D3D8DeviceLock AcquireLock() {
return m_protected
? D3D8DeviceLock(m_mutex)
: D3D8DeviceLock();
}

private:

BOOL m_protected;

sync::RecursiveSpinlock m_mutex;

};

}
55 changes: 55 additions & 0 deletions src/d3d8/d3d8_options.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include "d3d8_options.h"
#include "../d3d9/d3d9_bridge.h"
#include "../util/config/config.h"
#include "../util/util_string.h"

#include <charconv>

namespace dxvk {
static inline uint32_t parseDword(std::string_view str) {
uint32_t value = UINT32_MAX;
std::from_chars(str.data(), str.data() + str.size(), value);
return value;
}

void D3D8Options::parseVsDecl(const std::string& decl) {
if (decl.empty())
return;

if (decl.find_first_of("0123456789") == std::string::npos) {
Logger::warn(str::format("D3D8: Invalid forceVsDecl value: ", decl));
Logger::warn("D3D8: Expected numbers.");
return;
}

if (decl.find_first_of(":,;") == std::string::npos) {
Logger::warn(str::format("D3D8: Invalid forceVsDecl value: ", decl));
Logger::warn("D3D8: Expected a comma-separated list of colon-separated number pairs.");
return;
}

std::vector<std::string_view> decls = str::split(decl, ":,;");

if (decls.size() % 2 != 0) {
Logger::warn(str::format("D3D8: Invalid forceVsDecl value: ", decl));
Logger::warn("D3D8: Expected an even number of numbers.");
return;
}

for (size_t i = 0; i < decls.size(); i += 2) {
uint32_t reg = parseDword(decls[i]);
uint32_t type = parseDword(decls[i+1]);
if (reg > D3DVSDE_NORMAL2) {
Logger::warn(str::format("D3D8: Invalid forceVsDecl register number: ", decls[i]));
return;
}
if (type > D3DVSDT_SHORT4) {
Logger::warn(str::format("D3D8: Invalid forceVsDecl type: ", decls[i+1]));
return;
}

forceVsDecl.emplace_back(D3DVSDE_REGISTER(reg), D3DVSDT_TYPE(type));
}

}
}
56 changes: 56 additions & 0 deletions src/d3d8/d3d8_options.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#pragma once

#include "d3d8_include.h"
#include "../d3d9/d3d9_bridge.h"
#include "../util/config/config.h"

namespace dxvk {
struct D3D8Options {

/// Some games rely on undefined behavior by using undeclared vertex shader inputs.
/// The simplest way to fix them is to simply modify their vertex shader decl.
///
/// This option takes a comma-separated list of colon-separated number pairs, where
/// the first number is a D3DVSDE_REGISTER value, the second is a D3DVSDT_TYPE value.
/// e.g. "0:2,3:2,7:1" for float3 position : v0, float3 normal : v3, float2 uv : v7
std::vector<std::pair<D3DVSDE_REGISTER, D3DVSDT_TYPE>> forceVsDecl;

/// Specialized drawcall batcher, typically for games that draw a lot of similar
/// geometry in separate drawcalls (sometimes even one triangle at a time).
///
/// May hurt performance outside of specifc games that benefit from it.
bool batching = false;

/// The Lord of the Rings: The Fellowship of the Ring tries to create a P8 texture
/// in D3DPOOL_MANAGED on Nvidia and Intel, which fails, but has a separate code
/// path for ATI/AMD that creates it in D3DPOOL_SCRATCH instead, which works.
///
/// The internal logic determining this path doesn't seem to be d3d-related, but
/// the game works universally if we mimic its own ATI/AMD workaround during P8
/// texture creation.
///
/// Early Nvidia GPUs, such as the GeForce 4 generation cards, included and exposed
/// P8 texture support. However, it was no longer advertised with cards in the FX series
/// and above. Most likely ATI/AMD drivers never supported P8 in the first place.
bool placeP8InScratch = false;

/// Rayman 3 relies on D3DLOCK_DISCARD being ignored for everything except D3DUSAGE_DYNAMIC +
/// D3DUSAGE_WRITEONLY buffers, however this approach incurs a performance penalty.
///
/// Some titles might abuse this early D3D8 quirk, however at some point in its history
/// it was brought in line with standard D3D9 behavior.
bool forceLegacyDiscard = false;

D3D8Options() {}
D3D8Options(const Config& config) {
auto forceVsDeclStr = config.getOption<std::string>("d3d8.forceVsDecl", "");
batching = config.getOption<bool> ("d3d8.batching", batching);
placeP8InScratch = config.getOption<bool> ("d3d8.placeP8InScratch", placeP8InScratch);
forceLegacyDiscard = config.getOption<bool> ("d3d8.forceLegacyDiscard", forceLegacyDiscard);

parseVsDecl(forceVsDeclStr);
}

void parseVsDecl(const std::string& decl);
};
}
Loading