dxvk/src/d3d9/d3d9_common_texture.h

559 lines
16 KiB
C++

#pragma once
#include "d3d9_format.h"
#include "d3d9_util.h"
#include "d3d9_caps.h"
#include "d3d9_mem.h"
#include "d3d9_interop.h"
#include "../dxvk/dxvk_device.h"
#include "../util/util_bit.h"
namespace dxvk {
class D3D9DeviceEx;
/**
* \brief Image memory mapping mode
*
* Determines how exactly \c LockBox will
* behave when mapping an image.
*/
enum D3D9_COMMON_TEXTURE_MAP_MODE {
D3D9_COMMON_TEXTURE_MAP_MODE_NONE, ///< No mapping available
D3D9_COMMON_TEXTURE_MAP_MODE_BACKED, ///< Mapped image through buffer
D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM, ///< Only a buffer - no image
D3D9_COMMON_TEXTURE_MAP_MODE_UNMAPPABLE, ///< Non-Vulkan memory that can be unmapped
};
/**
* \brief Common texture description
*
* Contains all members that can be
* defined for 2D, Cube and 3D textures.
*/
struct D3D9_COMMON_TEXTURE_DESC {
UINT Width;
UINT Height;
UINT Depth;
UINT ArraySize;
UINT MipLevels;
DWORD Usage;
D3D9Format Format;
D3DPOOL Pool;
D3DMULTISAMPLE_TYPE MultiSample;
DWORD MultisampleQuality;
bool Discard;
bool IsBackBuffer;
bool IsAttachmentOnly;
bool IsLockable;
};
struct D3D9ColorView {
inline Rc<DxvkImageView>& Pick(bool Srgb) {
return Srgb ? this->Srgb : this->Color;
}
inline const Rc<DxvkImageView>& Pick(bool Srgb) const {
return Srgb ? this->Srgb : this->Color;
}
Rc<DxvkImageView> Color;
Rc<DxvkImageView> Srgb;
};
template <typename T>
using D3D9SubresourceArray = std::array<T, caps::MaxSubresources>;
using D3D9SubresourceBitset = bit::bitset<caps::MaxSubresources>;
class D3D9CommonTexture {
public:
D3D9CommonTexture(
D3D9DeviceEx* pDevice,
IUnknown* pInterface,
const D3D9_COMMON_TEXTURE_DESC* pDesc,
D3DRESOURCETYPE ResourceType,
HANDLE* pSharedHandle);
~D3D9CommonTexture();
/**
* \brief Device
* \returns The parent device
*/
D3D9DeviceEx* Device() const {
return m_device;
}
/**
* \brief Texture properties
*
* The returned data can be used to fill in
* \c D3D11_TEXTURE2D_DESC and similar structs.
* \returns Pointer to texture description
*/
const D3D9_COMMON_TEXTURE_DESC* Desc() const {
return &m_desc;
}
/**
* \brief Vulkan Format
* \returns The Vulkan format of the resource
*/
const D3D9_VK_FORMAT_MAPPING& GetFormatMapping() const {
return m_mapping;
}
/**
* \brief Counts number of subresources
* \returns Number of subresources
*/
UINT CountSubresources() const {
return m_desc.ArraySize * m_desc.MipLevels;
}
/**
* \brief Map mode
* \returns Map mode
*/
D3D9_COMMON_TEXTURE_MAP_MODE GetMapMode() const {
return m_mapMode;
}
/**
* \brief The DXVK image
* Note, this will be nullptr if the map mode is D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM
* \returns The DXVK image
*/
const Rc<DxvkImage>& GetImage() const {
return m_image;
}
/**
* \brief Get a copy of the main image, but with a single sample
* This function will allocate/reuse an image with the same info
* as the main image
* \returns An image with identical info, but 1 sample
*/
const Rc<DxvkImage>& GetResolveImage() {
if (unlikely(m_resolveImage == nullptr))
m_resolveImage = CreateResolveImage();
return m_resolveImage;
}
/**
* \brief Returns a pointer to the internal data used for LockRect/LockBox
*
* This works regardless of the map mode used by this texture
* and will map the memory if necessary.
* \param [in] Subresource Subresource index
* @return Pointer to locking data
*/
void* GetData(UINT Subresource);
const Rc<DxvkBuffer>& GetBuffer();
DxvkBufferSlice GetBufferSlice(UINT Subresource);
/**
* \brief Computes subresource from the subresource index
*
* Used by some functions that operate on only
* one subresource, such as \c UpdateSurface.
* \param [in] Aspect The image aspect
* \param [in] Subresource Subresource index
* \returns The Vulkan image subresource
*/
VkImageSubresource GetSubresourceFromIndex(
VkImageAspectFlags Aspect,
UINT Subresource) const;
/**
* \brief Normalizes and validates texture description
*
* Fills in undefined values and validates the texture
* parameters. Any error returned by this method should
* be forwarded to the application.
* \param [in,out] pDesc Texture description
* \returns \c S_OK if the parameters are valid
*/
static HRESULT NormalizeTextureProperties(
D3D9DeviceEx* pDevice,
D3D9_COMMON_TEXTURE_DESC* pDesc);
/**
* \brief Shadow
* \returns Whether the texture is to be depth compared
*/
bool IsShadow() const {
return m_shadow;
}
/**
* \brief Dref Clamp
* \returns Whether the texture emulates an UNORM format with D32f
*/
bool IsUpgradedToD32f() const {
return m_upgradedToD32f;
}
/**
* \brief FETCH4 compatibility
* \returns Whether the format of the texture supports the FETCH4 hack
*/
bool SupportsFetch4() const {
return m_supportsFetch4;
}
/**
* \brief Null
* \returns Whether the texture is D3DFMT_NULL or not
*/
bool IsNull() const {
return m_desc.Format == D3D9Format::NULL_FORMAT;
}
/**
* \brief Subresource
* \returns The subresource idx of a given face and mip level
*/
UINT CalcSubresource(UINT Face, UINT MipLevel) const {
return Face * m_desc.MipLevels + MipLevel;
}
void UnmapData() {
m_data.Unmap();
}
/**
* \brief Destroys a buffer
* Destroys mapping and staging buffers for a given subresource
*/
void DestroyBuffer() {
m_buffer = nullptr;
MarkAllNeedReadback();
}
bool IsDynamic() const {
return m_desc.Usage & D3DUSAGE_DYNAMIC;
}
/**
* \brief Managed
* \returns Whether a resource is managed (pool) or not
*/
bool IsManaged() const {
return IsPoolManaged(m_desc.Pool);
}
/**
* \brief Render Target
* \returns Whether a resource is a render target or not
*/
bool IsRenderTarget() const {
return m_desc.Usage & D3DUSAGE_RENDERTARGET;
}
/**
* \brief Depth stencil
* \returns Whether a resource is a depth stencil or not
*/
bool IsDepthStencil() const {
return m_desc.Usage & D3DUSAGE_DEPTHSTENCIL;
}
/**
* \brief Autogen Mipmap
* \returns Whether the texture is to have automatic mip generation
*/
bool IsAutomaticMip() const {
return m_desc.Usage & D3DUSAGE_AUTOGENMIPMAP;
}
/**
* \brief Checks whether sRGB views can be created
* \returns Whether the format is sRGB compatible.
*/
bool IsSrgbCompatible() const {
return m_mapping.FormatSrgb;
}
/**
* \brief Recreate main image view
* Recreates the main view of the sampler w/ a specific LOD.
* SetLOD only works on MANAGED textures so this is A-okay.
*/
void CreateSampleView(UINT Lod);
/**
* \brief Extent
* \returns The extent of the top-level mip
*/
VkExtent3D GetExtent() const {
return VkExtent3D{ m_desc.Width, m_desc.Height, m_desc.Depth };
}
/**
* \brief Mip Extent
* \returns The extent of a mip or subresource
*/
VkExtent3D GetExtentMip(UINT Subresource) const {
UINT MipLevel = Subresource % m_desc.MipLevels;
return util::computeMipLevelExtent(GetExtent(), MipLevel);
}
bool MarkHazardous() {
return std::exchange(m_hazardous, true);
}
D3DRESOURCETYPE GetType() {
return m_type;
}
const D3D9_VK_FORMAT_MAPPING& GetMapping() { return m_mapping; }
void SetLocked(UINT Subresource, bool value) { m_locked.set(Subresource, value); }
bool GetLocked(UINT Subresource) const { return m_locked.get(Subresource); }
bool IsAnySubresourceLocked() const { return m_locked.any(); }
void SetNeedsReadback(UINT Subresource, bool value) { m_needsReadback.set(Subresource, value); }
bool NeedsReadback(UINT Subresource) const { return m_needsReadback.get(Subresource); }
void MarkAllNeedReadback() { m_needsReadback.setAll(); }
void SetReadOnlyLocked(UINT Subresource, bool readOnly) { return m_readOnly.set(Subresource, readOnly); }
bool GetReadOnlyLocked(UINT Subresource) const { return m_readOnly.get(Subresource); }
const Rc<DxvkImageView>& GetSampleView(bool srgb) const {
return m_sampleView.Pick(srgb && IsSrgbCompatible());
}
VkImageLayout DetermineRenderTargetLayout(VkImageLayout hazardLayout) const {
if (unlikely(m_hazardous))
return hazardLayout;
return m_image != nullptr &&
m_image->info().tiling == VK_IMAGE_TILING_OPTIMAL
? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
: VK_IMAGE_LAYOUT_GENERAL;
}
VkImageLayout DetermineDepthStencilLayout(bool write, bool hazardous, VkImageLayout hazardLayout) const {
VkImageLayout layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
if (unlikely(hazardous)) {
layout = write
? hazardLayout
: VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL;
}
if (unlikely(m_image->info().tiling != VK_IMAGE_TILING_OPTIMAL))
layout = VK_IMAGE_LAYOUT_GENERAL;
return layout;
}
Rc<DxvkImageView> CreateView(
UINT Layer,
UINT Lod,
VkImageUsageFlags UsageFlags,
bool Srgb);
D3D9SubresourceBitset& GetUploadBitmask() { return m_needsUpload; }
void SetAllNeedUpload() {
m_needsUpload.setAll();
}
void SetNeedsUpload(UINT Subresource, bool upload) { m_needsUpload.set(Subresource, upload); }
bool NeedsUpload(UINT Subresource) const { return m_needsUpload.get(Subresource); }
bool NeedsAnyUpload() { return m_needsUpload.any(); }
void ClearNeedsUpload() { return m_needsUpload.clearAll(); }
void SetNeedsMipGen(bool value) { m_needsMipGen = value; }
bool NeedsMipGen() const { return m_needsMipGen; }
DWORD ExposedMipLevels() { return m_exposedMipLevels; }
void SetMipFilter(D3DTEXTUREFILTERTYPE filter) { m_mipFilter = filter; }
D3DTEXTUREFILTERTYPE GetMipFilter() const { return m_mipFilter; }
void PreLoadAll();
void PreLoadSubresource(UINT Subresource);
void AddDirtyBox(CONST D3DBOX* pDirtyBox, uint32_t layer) {
if (pDirtyBox) {
D3DBOX box = *pDirtyBox;
if (box.Right <= box.Left
|| box.Bottom <= box.Top
|| box.Back <= box.Front)
return;
box.Right = std::min(box.Right, m_desc.Width);
box.Bottom = std::min(box.Bottom, m_desc.Height);
box.Back = std::min(box.Back, m_desc.Depth);
D3DBOX& dirtyBox = m_dirtyBoxes[layer];
if (dirtyBox.Left == dirtyBox.Right) {
dirtyBox = box;
} else {
dirtyBox.Left = std::min(dirtyBox.Left, box.Left);
dirtyBox.Right = std::max(dirtyBox.Right, box.Right);
dirtyBox.Top = std::min(dirtyBox.Top, box.Top);
dirtyBox.Bottom = std::max(dirtyBox.Bottom, box.Bottom);
dirtyBox.Front = std::min(dirtyBox.Front, box.Front);
dirtyBox.Back = std::max(dirtyBox.Back, box.Back);
}
} else {
m_dirtyBoxes[layer] = { 0, 0, m_desc.Width, m_desc.Height, 0, m_desc.Depth };
}
}
void ClearDirtyBoxes() {
for (uint32_t i = 0; i < m_dirtyBoxes.size(); i++) {
m_dirtyBoxes[i] = { 0, 0, 0, 0, 0, 0 };
}
}
const D3DBOX& GetDirtyBox(uint32_t layer) const {
return m_dirtyBoxes[layer];
}
static VkImageType GetImageTypeFromResourceType(
D3DRESOURCETYPE Dimension);
/**
* \brief Tracks sequence number for a given subresource
*
* Stores which CS chunk the resource was last used on.
* \param [in] Subresource Subresource index
* \param [in] Seq Sequence number
*/
void TrackMappingBufferSequenceNumber(UINT Subresource, uint64_t Seq) {
if (Subresource < m_seqs.size())
m_seqs[Subresource] = Seq;
}
/**
* \brief Queries sequence number for a given subresource
*
* Returns which CS chunk the resource was last used on.
* \param [in] Subresource Subresource index
* \returns Sequence number for the given subresource
*/
uint64_t GetMappingBufferSequenceNumber(UINT Subresource) {
return Subresource < m_seqs.size()
? m_seqs[Subresource]
: 0ull;
}
/**
* \brief Mip level
* \returns Size of packed mip level in bytes
*/
VkDeviceSize GetMipSize(UINT Subresource) const;
uint32_t GetTotalSize() const {
return m_totalSize;
}
/**
* \brief Creates a buffer
* Creates the mapping buffer if necessary
* \param [in] Initialize Whether to copy over existing data (or clear if there is no data)
* \returns Whether an allocation happened
*/
void CreateBuffer(bool Initialize);
ID3D9VkInteropTexture* GetVkInterop() { return &m_d3d9Interop; }
private:
D3D9DeviceEx* m_device;
D3D9_COMMON_TEXTURE_DESC m_desc;
D3DRESOURCETYPE m_type;
D3D9_COMMON_TEXTURE_MAP_MODE m_mapMode;
Rc<DxvkImage> m_image;
Rc<DxvkImage> m_resolveImage;
Rc<DxvkBuffer> m_buffer;
D3D9Memory m_data = { };
D3D9SubresourceArray<
uint64_t> m_seqs = { };
D3D9SubresourceArray<
uint32_t> m_memoryOffset = { };
uint32_t m_totalSize = 0;
D3D9_VK_FORMAT_MAPPING m_mapping;
bool m_shadow; //< Depth Compare-ness
bool m_upgradedToD32f; // Dref Clamp
bool m_supportsFetch4;
int64_t m_size = 0;
bool m_hazardous = false;
D3D9ColorView m_sampleView;
D3D9SubresourceBitset m_locked = { };
D3D9SubresourceBitset m_readOnly = { };
D3D9SubresourceBitset m_needsReadback = { };
D3D9SubresourceBitset m_needsUpload = { };
DWORD m_exposedMipLevels = 0;
bool m_needsMipGen = false;
D3DTEXTUREFILTERTYPE m_mipFilter = D3DTEXF_LINEAR;
std::array<D3DBOX, 6> m_dirtyBoxes;
D3D9VkInteropTexture m_d3d9Interop;
Rc<DxvkImage> CreatePrimaryImage(D3DRESOURCETYPE ResourceType, bool TryOffscreenRT, HANDLE* pSharedHandle) const;
Rc<DxvkImage> CreateResolveImage() const;
BOOL DetermineShadowState() const;
BOOL DetermineFetch4Compatibility() const;
BOOL CheckImageSupport(
const DxvkImageCreateInfo* pImageInfo,
VkImageTiling Tiling) const;
VkImageUsageFlags EnableMetaCopyUsage(
VkFormat Format,
VkImageTiling Tiling) const;
D3D9_COMMON_TEXTURE_MAP_MODE DetermineMapMode() const;
VkImageLayout OptimizeLayout(
VkImageUsageFlags Usage) const;
void ExportImageInfo();
static VkImageViewType GetImageViewTypeFromResourceType(
D3DRESOURCETYPE Dimension,
UINT Layer);
static constexpr UINT AllLayers = UINT32_MAX;
};
}