1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-01-18 20:52:10 +01:00
dxvk/src/d3d9/d3d9_common_texture.cpp
Philip Rebohle 905d69e77b [d3d9] Don't pass format mapping to D3D9CommonTexture
Instead, infer it from the format. This is basically being done
already, however the mapping we pass in is not correct if the
image format is Unknown.
2020-01-17 17:47:08 +01:00

462 lines
17 KiB
C++

#include "d3d9_common_texture.h"
#include "d3d9_util.h"
#include "d3d9_device.h"
#include <algorithm>
namespace dxvk {
D3D9CommonTexture::D3D9CommonTexture(
D3D9DeviceEx* pDevice,
const D3D9_COMMON_TEXTURE_DESC* pDesc,
D3DRESOURCETYPE ResourceType)
: m_device(pDevice), m_desc(*pDesc), m_type(ResourceType) {
if (m_desc.Format == D3D9Format::Unknown)
m_desc.Format = (m_desc.Usage & D3DUSAGE_DEPTHSTENCIL)
? D3D9Format::D32
: D3D9Format::X8R8G8B8;
m_mapping = pDevice->LookupFormat(m_desc.Format);
auto pxSize = m_mapping.VideoFormatInfo.MacroPixelSize;
m_adjustedExtent = VkExtent3D{ m_desc.Width / pxSize.width, m_desc.Height / pxSize.height, m_desc.Depth };
m_mapMode = DetermineMapMode();
m_shadow = DetermineShadowState();
if (m_mapMode == D3D9_COMMON_TEXTURE_MAP_MODE_BACKED) {
try {
m_image = CreatePrimaryImage(ResourceType);
}
catch (const DxvkError& e) {
if (m_desc.Usage & D3DUSAGE_AUTOGENMIPMAP) {
m_desc.Usage &= ~D3DUSAGE_AUTOGENMIPMAP;
m_desc.MipLevels = 1;
m_image = CreatePrimaryImage(ResourceType);
}
else
throw e;
}
CreateSampleView(0);
if (!IsManaged()) {
m_size = m_image->memSize();
if (!m_device->ChangeReportedMemory(-m_size))
throw DxvkError("D3D9: Reporting out of memory from tracking.");
}
}
if (m_mapMode == D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM)
CreateBuffers();
}
D3D9CommonTexture::~D3D9CommonTexture() {
if (m_size != 0)
m_device->ChangeReportedMemory(m_size);
}
VkImageSubresource D3D9CommonTexture::GetSubresourceFromIndex(
VkImageAspectFlags Aspect,
UINT Subresource) const {
VkImageSubresource result;
result.aspectMask = Aspect;
result.mipLevel = Subresource % m_desc.MipLevels;
result.arrayLayer = Subresource / m_desc.MipLevels;
return result;
}
HRESULT D3D9CommonTexture::NormalizeTextureProperties(
D3D9DeviceEx* pDevice,
D3D9_COMMON_TEXTURE_DESC* pDesc) {
auto* options = pDevice->GetOptions();
//////////////////////
// Mapping Validation
auto mapping = pDevice->LookupFormat(pDesc->Format);
// Handle DisableA8RT hack for The Sims 2
if (pDesc->Format == D3D9Format::A8 &&
(pDesc->Usage & D3DUSAGE_RENDERTARGET) &&
options->disableA8RT)
return D3DERR_INVALIDCALL;
// If the mapping is invalid then lets return invalid
// Some edge cases:
// NULL format does not map to anything, but should succeed
// SCRATCH textures can still be made if the device does not support
// the format at all.
if (!mapping.IsValid() && pDesc->Format != D3D9Format::NULL_FORMAT) {
auto info = pDevice->UnsupportedFormatInfo(pDesc->Format);
if (pDesc->Pool != D3DPOOL_SCRATCH || info.elementSize == 0)
return D3DERR_INVALIDCALL;
}
///////////////////
// Desc Validation
if (pDesc->Width == 0 || pDesc->Height == 0 || pDesc->Depth == 0)
return D3DERR_INVALIDCALL;
if (FAILED(DecodeMultiSampleType(pDesc->MultiSample, pDesc->MultisampleQuality, nullptr)))
return D3DERR_INVALIDCALL;
// Using MANAGED pool with DYNAMIC usage is illegal
if (IsPoolManaged(pDesc->Pool) && (pDesc->Usage & D3DUSAGE_DYNAMIC))
return D3DERR_INVALIDCALL;
// D3DUSAGE_WRITEONLY doesn't apply to textures.
if (pDesc->Usage & D3DUSAGE_WRITEONLY)
return D3DERR_INVALIDCALL;
// RENDERTARGET and DEPTHSTENCIL must be default pool
constexpr DWORD incompatibleUsages = D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL;
if (pDesc->Pool != D3DPOOL_DEFAULT && (pDesc->Usage & incompatibleUsages))
return D3DERR_INVALIDCALL;
// Use the maximum possible mip level count if the supplied
// mip level count is either unspecified (0) or invalid
const uint32_t maxMipLevelCount =
(pDesc->MultiSample <= D3DMULTISAMPLE_NONMASKABLE && !(pDesc->Usage & D3DUSAGE_AUTOGENMIPMAP))
? util::computeMipLevelCount({ pDesc->Width, pDesc->Height, pDesc->Depth })
: 1u;
if (pDesc->MipLevels == 0 || pDesc->MipLevels > maxMipLevelCount)
pDesc->MipLevels = maxMipLevelCount;
return D3D_OK;
}
bool D3D9CommonTexture::CreateBufferSubresource(UINT Subresource) {
if (m_buffers[Subresource] != nullptr)
return false;
DxvkBufferCreateInfo info;
info.size = GetMipSize(Subresource);
info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT
| VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT;
info.access = VK_ACCESS_TRANSFER_READ_BIT
| VK_ACCESS_TRANSFER_WRITE_BIT;
if (m_mapping.VideoFormatInfo.FormatType != D3D9VideoFormat_None) {
info.usage |= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
info.stages |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
}
VkMemoryPropertyFlags memType = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
if (m_mapMode == D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM || IsManaged())
memType |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
m_buffers[Subresource] = m_device->GetDXVKDevice()->createBuffer(info, memType);
m_mappedSlices[Subresource] = m_buffers[Subresource]->getSliceHandle();
return true;
}
VkDeviceSize D3D9CommonTexture::GetMipSize(UINT Subresource) const {
const UINT MipLevel = Subresource % m_desc.MipLevels;
const DxvkFormatInfo formatInfo = m_mapping.FormatColor != VK_FORMAT_UNDEFINED
? *imageFormatInfo(m_mapping.FormatColor)
: m_device->UnsupportedFormatInfo(m_desc.Format);
const VkExtent3D mipExtent = util::computeMipLevelExtent(
m_adjustedExtent, MipLevel);
const VkExtent3D blockCount = util::computeBlockCount(
mipExtent, formatInfo.blockSize);
return formatInfo.elementSize
* blockCount.width
* blockCount.height
* blockCount.depth;
}
Rc<DxvkImage> D3D9CommonTexture::CreatePrimaryImage(D3DRESOURCETYPE ResourceType) const {
DxvkImageCreateInfo imageInfo;
imageInfo.type = GetImageTypeFromResourceType(ResourceType);
imageInfo.format = m_mapping.FormatColor;
imageInfo.flags = 0;
imageInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;
imageInfo.extent.width = m_desc.Width;
imageInfo.extent.height = m_desc.Height;
imageInfo.extent.depth = m_desc.Depth;
imageInfo.numLayers = m_desc.ArraySize;
imageInfo.mipLevels = m_desc.MipLevels;
imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT
| VK_IMAGE_USAGE_TRANSFER_DST_BIT
| VK_IMAGE_USAGE_SAMPLED_BIT;
imageInfo.stages = VK_PIPELINE_STAGE_TRANSFER_BIT
| m_device->GetEnabledShaderStages();
imageInfo.access = VK_ACCESS_TRANSFER_READ_BIT
| VK_ACCESS_TRANSFER_WRITE_BIT
| VK_ACCESS_SHADER_READ_BIT;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.layout = VK_IMAGE_LAYOUT_GENERAL;
if (m_mapping.VideoFormatInfo.FormatType != D3D9VideoFormat_None) {
imageInfo.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
imageInfo.stages |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
}
DecodeMultiSampleType(m_desc.MultiSample, m_desc.MultisampleQuality, &imageInfo.sampleCount);
// The image must be marked as mutable if it can be reinterpreted
// by a view with a different format. Depth-stencil formats cannot
// be reinterpreted in Vulkan, so we'll ignore those.
auto formatProperties = imageFormatInfo(m_mapping.FormatColor);
bool isMutable = m_mapping.FormatSrgb != VK_FORMAT_UNDEFINED;
bool isColorFormat = (formatProperties->aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0;
if (isMutable && isColorFormat) {
imageInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
imageInfo.viewFormatCount = 2;
imageInfo.viewFormats = m_mapping.Formats;
}
if (m_desc.Usage & D3DUSAGE_RENDERTARGET || m_desc.Usage & D3DUSAGE_AUTOGENMIPMAP) {
imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
imageInfo.stages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
imageInfo.access |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
}
if (m_desc.Usage & D3DUSAGE_DEPTHSTENCIL) {
imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
imageInfo.stages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
| VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
imageInfo.access |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
}
if (ResourceType == D3DRTYPE_CUBETEXTURE)
imageInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
// Some image formats (i.e. the R32G32B32 ones) are
// only supported with linear tiling on most GPUs
if (!CheckImageSupport(&imageInfo, VK_IMAGE_TILING_OPTIMAL))
imageInfo.tiling = VK_IMAGE_TILING_LINEAR;
// We must keep LINEAR images in GENERAL layout, but we
// can choose a better layout for the image based on how
// it is going to be used by the game.
if (imageInfo.tiling == VK_IMAGE_TILING_OPTIMAL)
imageInfo.layout = OptimizeLayout(imageInfo.usage);
// For some formats, we need to enable render target
// capabilities if available, but these should
// in no way affect the default image layout
imageInfo.usage |= EnableMetaCopyUsage(imageInfo.format, imageInfo.tiling);
// Check if we can actually create the image
if (!CheckImageSupport(&imageInfo, imageInfo.tiling)) {
throw DxvkError(str::format(
"D3D9: Cannot create texture:",
"\n Type: ", std::hex, ResourceType,
"\n Format: ", m_desc.Format,
"\n Extent: ", m_desc.Width,
"x", m_desc.Height,
"x", m_desc.Depth,
"\n Samples: ", m_desc.MultiSample,
"\n Layers: ", m_desc.ArraySize,
"\n Levels: ", m_desc.MipLevels,
"\n Usage: ", std::hex, m_desc.Usage,
"\n Pool: ", std::hex, m_desc.Pool));
}
return m_device->GetDXVKDevice()->createImage(imageInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
}
Rc<DxvkImage> D3D9CommonTexture::CreateResolveImage() const {
DxvkImageCreateInfo imageInfo = m_image->info();
imageInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;
return m_device->GetDXVKDevice()->createImage(imageInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
}
BOOL D3D9CommonTexture::DetermineShadowState() const {
static std::array<D3D9Format, 3> blacklist = {
D3D9Format::INTZ, D3D9Format::DF16, D3D9Format::DF24
};
return IsDepthFormat(m_desc.Format)
&& std::find(blacklist.begin(), blacklist.end(), m_desc.Format) == blacklist.end();
}
BOOL D3D9CommonTexture::CheckImageSupport(
const DxvkImageCreateInfo* pImageInfo,
VkImageTiling Tiling) const {
const Rc<DxvkAdapter> adapter = m_device->GetDXVKDevice()->adapter();
VkImageFormatProperties formatProps = { };
VkResult status = adapter->imageFormatProperties(
pImageInfo->format, pImageInfo->type, Tiling,
pImageInfo->usage, pImageInfo->flags, formatProps);
if (status != VK_SUCCESS)
return FALSE;
return (pImageInfo->extent.width <= formatProps.maxExtent.width)
&& (pImageInfo->extent.height <= formatProps.maxExtent.height)
&& (pImageInfo->extent.depth <= formatProps.maxExtent.depth)
&& (pImageInfo->numLayers <= formatProps.maxArrayLayers)
&& (pImageInfo->mipLevels <= formatProps.maxMipLevels)
&& (pImageInfo->sampleCount & formatProps.sampleCounts);
}
VkImageUsageFlags D3D9CommonTexture::EnableMetaCopyUsage(
VkFormat Format,
VkImageTiling Tiling) const {
VkFormatFeatureFlags requestedFeatures = 0;
if (Format == VK_FORMAT_D16_UNORM || Format == VK_FORMAT_D32_SFLOAT)
requestedFeatures |= VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
if (Format == VK_FORMAT_R16_UNORM || Format == VK_FORMAT_R32_SFLOAT)
requestedFeatures |= VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
if (requestedFeatures == 0)
return 0;
// Enable usage flags for all supported and requested features
VkFormatProperties properties = m_device->GetDXVKDevice()->adapter()->formatProperties(Format);
requestedFeatures &= Tiling == VK_IMAGE_TILING_OPTIMAL
? properties.optimalTilingFeatures
: properties.linearTilingFeatures;
VkImageUsageFlags requestedUsage = 0;
if (requestedFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
requestedUsage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
if (requestedFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)
requestedUsage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
return requestedUsage;
}
VkImageType D3D9CommonTexture::GetImageTypeFromResourceType(D3DRESOURCETYPE Type) {
switch (Type) {
case D3DRTYPE_TEXTURE: return VK_IMAGE_TYPE_2D;
case D3DRTYPE_VOLUMETEXTURE: return VK_IMAGE_TYPE_3D;
case D3DRTYPE_CUBETEXTURE: return VK_IMAGE_TYPE_2D;
default: throw DxvkError("D3D9CommonTexture: Unhandled resource type");
}
}
VkImageViewType D3D9CommonTexture::GetImageViewTypeFromResourceType(
D3DRESOURCETYPE Dimension,
UINT Layer) {
switch (Dimension) {
case D3DRTYPE_TEXTURE: return VK_IMAGE_VIEW_TYPE_2D;
case D3DRTYPE_VOLUMETEXTURE: return VK_IMAGE_VIEW_TYPE_3D;
case D3DRTYPE_CUBETEXTURE: return Layer == AllLayers
? VK_IMAGE_VIEW_TYPE_CUBE
: VK_IMAGE_VIEW_TYPE_2D;
default: throw DxvkError("D3D9CommonTexture: Unhandled resource type");
}
}
VkImageLayout D3D9CommonTexture::OptimizeLayout(VkImageUsageFlags Usage) {
const VkImageUsageFlags usageFlags = Usage;
// Filter out unnecessary flags. Transfer operations
// are handled by the backend in a transparent manner.
Usage &= ~(VK_IMAGE_USAGE_TRANSFER_DST_BIT
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
// If the image is used only as an attachment, we never
// have to transform the image back to a different layout
if (Usage == VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
if (Usage == VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
Usage &= ~(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
| VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
// If the image is used for reading but not as a storage
// image, we can optimize the image for texture access
if (Usage == VK_IMAGE_USAGE_SAMPLED_BIT) {
return usageFlags & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT
? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL
: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
// Otherwise, we have to stick with the default layout
return VK_IMAGE_LAYOUT_GENERAL;
}
Rc<DxvkImageView> D3D9CommonTexture::CreateView(
UINT Layer,
UINT Lod,
VkImageUsageFlags UsageFlags,
bool Srgb) {
DxvkImageViewCreateInfo viewInfo;
viewInfo.format = PickSRGB(m_mapping.FormatColor, m_mapping.FormatSrgb, Srgb);
viewInfo.aspect = imageFormatInfo(viewInfo.format)->aspectMask;
viewInfo.swizzle = m_mapping.Swizzle;
viewInfo.usage = UsageFlags;
viewInfo.type = GetImageViewTypeFromResourceType(m_type, Layer);
viewInfo.minLevel = Lod;
viewInfo.numLevels = m_desc.MipLevels - Lod;
viewInfo.minLayer = Layer == AllLayers ? 0 : Layer;
viewInfo.numLayers = Layer == AllLayers ? m_desc.ArraySize : 1;
// Remove the stencil aspect if we are trying to create a regular image
// view of a depth stencil format
if (UsageFlags != VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
viewInfo.aspect &= ~VK_IMAGE_ASPECT_STENCIL_BIT;
if (UsageFlags == VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT ||
UsageFlags == VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
viewInfo.numLevels = 1;
// Remove swizzle on depth views.
if (UsageFlags == VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
viewInfo.swizzle = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };
// Create the underlying image view object
return m_device->GetDXVKDevice()->createImageView(GetImage(), viewInfo);
}
void D3D9CommonTexture::CreateSampleView(UINT Lod) {
// This will be a no-op for SYSTEMMEM types given we
// don't expose the cap to allow texturing with them.
if (unlikely(m_mapMode == D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM))
return;
m_sampleView.Color = CreateView(AllLayers, Lod, VK_IMAGE_USAGE_SAMPLED_BIT, false);
m_sampleView.Srgb = CreateView(AllLayers, Lod, VK_IMAGE_USAGE_SAMPLED_BIT, true);
}
}