2018-03-03 20:59:17 +01:00
|
|
|
#include "d3d11_cmdlist.h"
|
2018-01-20 13:22:44 +01:00
|
|
|
#include "d3d11_context_imm.h"
|
|
|
|
#include "d3d11_device.h"
|
|
|
|
#include "d3d11_texture.h"
|
|
|
|
|
|
|
|
namespace dxvk {
|
|
|
|
|
|
|
|
D3D11ImmediateContext::D3D11ImmediateContext(
|
2018-01-23 09:23:31 +01:00
|
|
|
D3D11Device* pParent,
|
|
|
|
Rc<DxvkDevice> Device)
|
|
|
|
: D3D11DeviceContext(pParent, Device),
|
|
|
|
m_csThread(Device->createContext()) {
|
2018-01-20 13:22:44 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
D3D11ImmediateContext::~D3D11ImmediateContext() {
|
2018-01-20 23:12:03 +01:00
|
|
|
Flush();
|
2018-01-21 18:04:22 +01:00
|
|
|
SynchronizeCsThread();
|
|
|
|
SynchronizeDevice();
|
2018-01-20 13:22:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ULONG STDMETHODCALLTYPE D3D11ImmediateContext::AddRef() {
|
|
|
|
return m_parent->AddRef();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ULONG STDMETHODCALLTYPE D3D11ImmediateContext::Release() {
|
|
|
|
return m_parent->Release();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
D3D11_DEVICE_CONTEXT_TYPE STDMETHODCALLTYPE D3D11ImmediateContext::GetType() {
|
|
|
|
return D3D11_DEVICE_CONTEXT_IMMEDIATE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UINT STDMETHODCALLTYPE D3D11ImmediateContext::GetContextFlags() {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void STDMETHODCALLTYPE D3D11ImmediateContext::Flush() {
|
2018-03-11 20:25:09 +01:00
|
|
|
m_parent->FlushInitContext();
|
|
|
|
|
2018-02-19 11:27:14 +01:00
|
|
|
if (m_csChunk->commandCount() != 0) {
|
|
|
|
m_drawCount = 0;
|
2018-01-20 18:01:43 +01:00
|
|
|
|
2018-02-19 11:27:14 +01:00
|
|
|
// Add commands to flush the threaded
|
|
|
|
// context, then flush the command list
|
|
|
|
EmitCs([dev = m_device] (DxvkContext* ctx) {
|
|
|
|
dev->submitCommandList(
|
|
|
|
ctx->endRecording(),
|
|
|
|
nullptr, nullptr);
|
|
|
|
|
|
|
|
ctx->beginRecording(
|
|
|
|
dev->createCommandList());
|
|
|
|
});
|
|
|
|
|
|
|
|
FlushCsChunk();
|
|
|
|
}
|
2018-01-20 13:22:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void STDMETHODCALLTYPE D3D11ImmediateContext::ExecuteCommandList(
|
|
|
|
ID3D11CommandList* pCommandList,
|
2018-03-06 18:34:34 +01:00
|
|
|
BOOL RestoreContextState) {
|
2018-03-12 23:36:55 +01:00
|
|
|
FlushCsChunk();
|
|
|
|
|
2018-03-03 20:59:17 +01:00
|
|
|
static_cast<D3D11CommandList*>(pCommandList)->EmitToCsThread(&m_csThread);
|
|
|
|
|
|
|
|
if (RestoreContextState)
|
|
|
|
RestoreState();
|
|
|
|
else
|
|
|
|
ClearState();
|
2018-01-20 13:22:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE D3D11ImmediateContext::FinishCommandList(
|
2018-03-06 18:34:34 +01:00
|
|
|
BOOL RestoreDeferredContextState,
|
2018-01-20 13:22:44 +01:00
|
|
|
ID3D11CommandList **ppCommandList) {
|
|
|
|
Logger::err("D3D11: FinishCommandList called on immediate context");
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE D3D11ImmediateContext::Map(
|
|
|
|
ID3D11Resource* pResource,
|
|
|
|
UINT Subresource,
|
|
|
|
D3D11_MAP MapType,
|
|
|
|
UINT MapFlags,
|
|
|
|
D3D11_MAPPED_SUBRESOURCE* pMappedResource) {
|
2018-03-11 23:54:03 +01:00
|
|
|
if (pResource == nullptr) {
|
|
|
|
Logger::warn("D3D11ImmediateContext::Map() application tried to map a nullptr resource");
|
|
|
|
return DXGI_ERROR_INVALID_CALL;
|
|
|
|
}
|
|
|
|
|
2018-01-20 13:22:44 +01:00
|
|
|
D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;
|
|
|
|
pResource->GetType(&resourceDim);
|
|
|
|
|
|
|
|
if (resourceDim == D3D11_RESOURCE_DIMENSION_BUFFER) {
|
2018-03-14 14:40:09 +01:00
|
|
|
return MapBuffer(
|
|
|
|
static_cast<D3D11Buffer*>(pResource),
|
|
|
|
MapType, MapFlags, pMappedResource);
|
2018-01-20 13:22:44 +01:00
|
|
|
} else {
|
2018-03-14 14:40:09 +01:00
|
|
|
return MapImage(
|
|
|
|
GetCommonTexture(pResource),
|
|
|
|
Subresource, MapType, MapFlags,
|
|
|
|
pMappedResource);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void STDMETHODCALLTYPE D3D11ImmediateContext::Unmap(
|
|
|
|
ID3D11Resource* pResource,
|
|
|
|
UINT Subresource) {
|
|
|
|
D3D11_RESOURCE_DIMENSION resourceDim = D3D11_RESOURCE_DIMENSION_UNKNOWN;
|
|
|
|
pResource->GetType(&resourceDim);
|
|
|
|
|
|
|
|
if (resourceDim != D3D11_RESOURCE_DIMENSION_BUFFER)
|
|
|
|
UnmapImage(GetCommonTexture(pResource), Subresource);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT D3D11ImmediateContext::MapBuffer(
|
|
|
|
D3D11Buffer* pResource,
|
|
|
|
D3D11_MAP MapType,
|
|
|
|
UINT MapFlags,
|
|
|
|
D3D11_MAPPED_SUBRESOURCE* pMappedResource) {
|
|
|
|
Rc<DxvkBuffer> buffer = pResource->GetBufferSlice().buffer();
|
|
|
|
|
|
|
|
if (!(buffer->memFlags() & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) {
|
|
|
|
Logger::err("D3D11: Cannot map a device-local buffer");
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pMappedResource == nullptr)
|
|
|
|
return S_FALSE;
|
|
|
|
|
|
|
|
if (MapType == D3D11_MAP_WRITE_DISCARD) {
|
|
|
|
// Allocate a new backing slice for the buffer and set
|
|
|
|
// it as the 'new' mapped slice. This assumes that the
|
|
|
|
// only way to invalidate a buffer is by mapping it.
|
|
|
|
auto physicalSlice = buffer->allocPhysicalSlice();
|
|
|
|
physicalSlice.resource()->acquire();
|
2018-03-14 00:45:07 +01:00
|
|
|
|
2018-03-14 14:40:09 +01:00
|
|
|
pResource->GetBufferInfo()->mappedSlice = physicalSlice;
|
2018-03-14 00:45:07 +01:00
|
|
|
|
2018-03-14 14:40:09 +01:00
|
|
|
EmitCs([
|
|
|
|
cBuffer = buffer,
|
|
|
|
cPhysicalSlice = physicalSlice
|
|
|
|
] (DxvkContext* ctx) {
|
|
|
|
ctx->invalidateBuffer(cBuffer, cPhysicalSlice);
|
|
|
|
cPhysicalSlice.resource()->release();
|
|
|
|
});
|
|
|
|
} else if (MapType != D3D11_MAP_WRITE_NO_OVERWRITE) {
|
|
|
|
if (!WaitForResource(buffer->resource(), MapFlags))
|
|
|
|
return DXGI_ERROR_WAS_STILL_DRAWING;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use map pointer from previous map operation. This
|
|
|
|
// way we don't have to synchronize with the CS thread
|
|
|
|
// if the map mode is D3D11_MAP_WRITE_NO_OVERWRITE.
|
|
|
|
const DxvkPhysicalBufferSlice physicalSlice
|
|
|
|
= pResource->GetBufferInfo()->mappedSlice;
|
|
|
|
|
|
|
|
pMappedResource->pData = physicalSlice.mapPtr(0);
|
|
|
|
pMappedResource->RowPitch = physicalSlice.length();
|
|
|
|
pMappedResource->DepthPitch = physicalSlice.length();
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HRESULT D3D11ImmediateContext::MapImage(
|
|
|
|
D3D11CommonTexture* pResource,
|
|
|
|
UINT Subresource,
|
|
|
|
D3D11_MAP MapType,
|
|
|
|
UINT MapFlags,
|
|
|
|
D3D11_MAPPED_SUBRESOURCE* pMappedResource) {
|
|
|
|
const Rc<DxvkImage> mappedImage = pResource->GetImage();
|
|
|
|
const Rc<DxvkBuffer> mappedBuffer = pResource->GetMappedBuffer();
|
|
|
|
|
|
|
|
if (pResource->GetMapMode() == D3D11_COMMON_TEXTURE_MAP_MODE_NONE) {
|
|
|
|
Logger::err("D3D11: Cannot map a device-local image");
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pMappedResource == nullptr)
|
|
|
|
return S_FALSE;
|
|
|
|
|
2018-03-14 16:40:28 +01:00
|
|
|
// Parameter validation was successful
|
2018-03-14 14:40:09 +01:00
|
|
|
VkImageSubresource subresource =
|
|
|
|
pResource->GetSubresourceFromIndex(
|
|
|
|
VK_IMAGE_ASPECT_COLOR_BIT, Subresource);
|
|
|
|
|
|
|
|
pResource->SetMappedSubresource(subresource);
|
|
|
|
|
2018-03-14 16:40:28 +01:00
|
|
|
if (pResource->GetMapMode() == D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT) {
|
|
|
|
const VkImageType imageType = mappedImage->info().type;
|
2018-03-14 00:45:07 +01:00
|
|
|
|
2018-03-14 16:40:28 +01:00
|
|
|
// Wait for the resource to become available
|
|
|
|
if (!WaitForResource(mappedImage, MapFlags))
|
|
|
|
return DXGI_ERROR_WAS_STILL_DRAWING;
|
|
|
|
|
|
|
|
// Query the subresource's memory layout and hope that
|
|
|
|
// the application respects the returned pitch values.
|
|
|
|
VkSubresourceLayout layout = mappedImage->querySubresourceLayout(subresource);
|
|
|
|
|
|
|
|
pMappedResource->pData = mappedImage->mapPtr(layout.offset);
|
|
|
|
pMappedResource->RowPitch = imageType >= VK_IMAGE_TYPE_2D ? layout.rowPitch : layout.size;
|
|
|
|
pMappedResource->DepthPitch = imageType >= VK_IMAGE_TYPE_3D ? layout.depthPitch : layout.size;
|
|
|
|
return S_OK;
|
2018-03-14 14:40:09 +01:00
|
|
|
} else {
|
2018-03-14 16:40:28 +01:00
|
|
|
// Query format info which we need to compute
|
|
|
|
// the row pitch and layer pitch properly.
|
|
|
|
const DxvkFormatInfo* formatInfo = imageFormatInfo(mappedImage->info().format);
|
|
|
|
|
|
|
|
const VkExtent3D levelExtent = mappedImage->mipLevelExtent(subresource.mipLevel);
|
|
|
|
const VkExtent3D blockCount = util::computeBlockCount(
|
|
|
|
levelExtent, formatInfo->blockSize);
|
|
|
|
|
|
|
|
DxvkPhysicalBufferSlice physicalSlice;
|
2018-03-14 00:45:07 +01:00
|
|
|
|
2018-03-14 16:40:28 +01:00
|
|
|
if (MapType == D3D11_MAP_WRITE_DISCARD) {
|
|
|
|
// We do not have to preserve the contents of the
|
|
|
|
// buffer if the entire image gets discarded.
|
|
|
|
physicalSlice = mappedBuffer->allocPhysicalSlice();
|
|
|
|
physicalSlice.resource()->acquire();
|
2018-03-10 23:32:15 +01:00
|
|
|
|
2018-03-14 00:45:07 +01:00
|
|
|
EmitCs([
|
2018-03-14 16:40:28 +01:00
|
|
|
cImageBuffer = mappedBuffer,
|
|
|
|
cPhysicalSlice = physicalSlice
|
2018-03-14 00:45:07 +01:00
|
|
|
] (DxvkContext* ctx) {
|
2018-03-14 16:40:28 +01:00
|
|
|
ctx->invalidateBuffer(cImageBuffer, cPhysicalSlice);
|
|
|
|
cPhysicalSlice.resource()->release();
|
2018-03-14 00:45:07 +01:00
|
|
|
});
|
2018-03-14 16:40:28 +01:00
|
|
|
} else {
|
|
|
|
// When using any map mode which requires the image contents
|
|
|
|
// to be preserved, and if the GPU has write access to the
|
|
|
|
// image, copy the current image contents into the buffer.
|
|
|
|
const bool copyExistingData = pResource->Desc()->Usage == D3D11_USAGE_STAGING;
|
|
|
|
|
|
|
|
if (copyExistingData) {
|
|
|
|
const VkImageSubresourceLayers subresourceLayers = {
|
|
|
|
subresource.aspectMask,
|
|
|
|
subresource.mipLevel,
|
|
|
|
subresource.arrayLayer, 1 };
|
|
|
|
|
|
|
|
EmitCs([
|
|
|
|
cImageBuffer = mappedBuffer,
|
|
|
|
cImage = mappedImage,
|
|
|
|
cSubresources = subresourceLayers,
|
|
|
|
cLevelExtent = levelExtent
|
|
|
|
] (DxvkContext* ctx) {
|
|
|
|
ctx->copyImageToBuffer(
|
|
|
|
cImageBuffer, 0, VkExtent2D { 0u, 0u },
|
|
|
|
cImage, cSubresources, VkOffset3D { 0, 0, 0 },
|
|
|
|
cLevelExtent);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!WaitForResource(mappedBuffer->resource(), MapFlags))
|
|
|
|
return DXGI_ERROR_WAS_STILL_DRAWING;
|
|
|
|
|
|
|
|
physicalSlice = mappedBuffer->slice();
|
2018-01-20 13:22:44 +01:00
|
|
|
}
|
2018-03-14 00:45:07 +01:00
|
|
|
|
2018-03-14 16:40:28 +01:00
|
|
|
// Set up map pointer. Data is tightly packed within the mapped buffer.
|
|
|
|
pMappedResource->pData = physicalSlice.mapPtr(0);
|
|
|
|
pMappedResource->RowPitch = formatInfo->elementSize * blockCount.width;
|
|
|
|
pMappedResource->DepthPitch = formatInfo->elementSize * blockCount.width * blockCount.height;
|
|
|
|
return S_OK;
|
2018-01-20 13:22:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-14 14:40:09 +01:00
|
|
|
void D3D11ImmediateContext::UnmapImage(
|
|
|
|
D3D11CommonTexture* pResource,
|
2018-01-20 13:22:44 +01:00
|
|
|
UINT Subresource) {
|
2018-03-14 14:40:09 +01:00
|
|
|
if (pResource->GetMapMode() == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER) {
|
2018-01-20 13:22:44 +01:00
|
|
|
// Now that data has been written into the buffer,
|
|
|
|
// we need to copy its contents into the image
|
2018-03-14 14:40:09 +01:00
|
|
|
const Rc<DxvkImage> mappedImage = pResource->GetImage();
|
|
|
|
const Rc<DxvkBuffer> mappedBuffer = pResource->GetMappedBuffer();
|
2018-03-14 00:45:07 +01:00
|
|
|
|
2018-03-14 14:40:09 +01:00
|
|
|
VkImageSubresource subresource = pResource->GetMappedSubresource();
|
2018-03-14 00:45:07 +01:00
|
|
|
|
|
|
|
VkExtent3D levelExtent = mappedImage
|
|
|
|
->mipLevelExtent(subresource.mipLevel);
|
|
|
|
|
|
|
|
VkImageSubresourceLayers subresourceLayers = {
|
|
|
|
subresource.aspectMask,
|
|
|
|
subresource.mipLevel,
|
|
|
|
subresource.arrayLayer, 1 };
|
|
|
|
|
|
|
|
EmitCs([
|
|
|
|
cSrcBuffer = mappedBuffer,
|
|
|
|
cDstImage = mappedImage,
|
|
|
|
cDstLayers = subresourceLayers,
|
|
|
|
cDstLevelExtent = levelExtent
|
|
|
|
] (DxvkContext* ctx) {
|
|
|
|
ctx->copyBufferToImage(cDstImage, cDstLayers,
|
|
|
|
VkOffset3D { 0, 0, 0 }, cDstLevelExtent,
|
|
|
|
cSrcBuffer, 0, { 0u, 0u });
|
|
|
|
});
|
2018-01-20 13:22:44 +01:00
|
|
|
}
|
2018-03-14 16:40:28 +01:00
|
|
|
|
|
|
|
pResource->ClearMappedSubresource();
|
2018-01-20 13:22:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-01-21 18:04:22 +01:00
|
|
|
void D3D11ImmediateContext::SynchronizeCsThread() {
|
|
|
|
// Dispatch current chunk so that all commands
|
|
|
|
// recorded prior to this function will be run
|
2018-01-23 12:03:26 +01:00
|
|
|
FlushCsChunk();
|
2018-01-21 18:04:22 +01:00
|
|
|
|
|
|
|
m_csThread.synchronize();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void D3D11ImmediateContext::SynchronizeDevice() {
|
2018-01-20 13:22:44 +01:00
|
|
|
m_device->waitForIdle();
|
|
|
|
}
|
|
|
|
|
2018-01-20 22:52:18 +01:00
|
|
|
|
2018-03-10 23:32:15 +01:00
|
|
|
bool D3D11ImmediateContext::WaitForResource(
|
|
|
|
const Rc<DxvkResource>& Resource,
|
|
|
|
UINT MapFlags) {
|
|
|
|
// Wait for the any pending D3D11 command to be executed
|
|
|
|
// on the CS thread so that we can determine whether the
|
|
|
|
// resource is currently in use or not.
|
|
|
|
Flush();
|
|
|
|
SynchronizeCsThread();
|
|
|
|
|
|
|
|
if (Resource->isInUse()) {
|
|
|
|
// TODO implement properly in DxvkDevice
|
|
|
|
while (Resource->isInUse())
|
|
|
|
std::this_thread::yield();
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-01-23 12:03:26 +01:00
|
|
|
void D3D11ImmediateContext::EmitCsChunk(Rc<DxvkCsChunk>&& chunk) {
|
|
|
|
m_csThread.dispatchChunk(std::move(chunk));
|
2018-01-20 22:52:18 +01:00
|
|
|
}
|
|
|
|
|
2018-01-20 13:22:44 +01:00
|
|
|
}
|