Implement GrD3DGpu::onReadPixels and GrD3DGpu::onWritePixels.

* Add resource copy routines to command list
* Be sure to release resources from command list

Change-Id: I174c5a026d5e2289e6db56215b0d140c19ccf39e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/284156
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
This commit is contained in:
Jim Van Verth 2020-04-21 08:56:47 -04:00 committed by Skia Commit-Bot
parent 1ba00c7121
commit ba7f22954d
6 changed files with 263 additions and 9 deletions

View File

@ -56,7 +56,7 @@ sk_sp<GrD3DBuffer::Resource> GrD3DBuffer::Resource::Make(GrD3DGpu* gpu, size_t s
bufferDesc.Format = DXGI_FORMAT_UNKNOWN;
bufferDesc.SampleDesc.Count = 1;
bufferDesc.SampleDesc.Quality = 0; // Doesn't apply to buffers
bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; // use driver-selected swizzle
bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
bufferDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
ID3D12Resource* resource;
@ -227,7 +227,8 @@ void GrD3DBuffer::internalUnmap(size_t size) {
} else {
D3D12_RANGE range;
range.Begin = 0;
range.End = size;
// For READBACK heaps, unmap requires an empty range
range.End = fResourceState == D3D12_RESOURCE_STATE_COPY_DEST ? 0 : size;
fMappedResource->fD3DResource->Unmap(0, &range);
}

View File

@ -50,6 +50,9 @@ GrD3DCaps::GrD3DCaps(const GrContextOptions& contextOptions, IDXGIAdapter1* adap
// TODO: implement
fDynamicStateArrayGeometryProcessorTextureSupport = false;
// TODO: Can re-enable when fillRectToRect draw with shader works
fAvoidWritePixelsFastPath = true;
fShaderCaps.reset(new GrShaderCaps(contextOptions));
this->init(contextOptions, adapter, device);

View File

@ -48,12 +48,17 @@ void GrD3DCommandList::reset() {
SkDEBUGCODE(hr = ) fCommandList->Reset(fAllocator.get(), nullptr);
SkASSERT(SUCCEEDED(hr));
this->releaseResources();
SkDEBUGCODE(fIsActive = true;)
fHasWork = false;
}
void GrD3DCommandList::releaseResources() {
TRACE_EVENT0("skia.gpu", TRACE_FUNC);
if (fTrackedResources.count() == 0 && fTrackedRecycledResources.count() == 0) {
return;
}
SkASSERT(!fIsActive);
for (int i = 0; i < fTrackedResources.count(); ++i) {
fTrackedResources[i]->notifyFinishedWithWorkOnGpu();
@ -109,6 +114,46 @@ void GrD3DCommandList::submitResourceBarriers() {
SkASSERT(!fResourceBarriers.count());
}
void GrD3DCommandList::copyBufferToTexture(GrD3DBuffer* srcBuffer,
GrD3DTextureResource* dstTexture,
uint32_t subresourceCount,
D3D12_PLACED_SUBRESOURCE_FOOTPRINT* bufferFootprints,
int left, int top) {
SkASSERT(fIsActive);
SkASSERT(subresourceCount == 1 || (left == 0 && top == 0));
this->addingWork();
this->addResource(srcBuffer->resource());
this->addResource(dstTexture->resource());
for (uint32_t subresource = 0; subresource < subresourceCount; ++subresource) {
D3D12_TEXTURE_COPY_LOCATION src = {};
src.pResource = srcBuffer->d3dResource();
src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
src.PlacedFootprint = bufferFootprints[subresource];
D3D12_TEXTURE_COPY_LOCATION dst = {};
dst.pResource = dstTexture->d3dResource();
dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
dst.SubresourceIndex = subresource;
fCommandList->CopyTextureRegion(&dst, left, top, 0, &src, nullptr);
}
}
void GrD3DCommandList::copyTextureRegion(const GrManagedResource* dst,
const D3D12_TEXTURE_COPY_LOCATION* dstLocation,
UINT dstX, UINT dstY,
const GrManagedResource* src,
const D3D12_TEXTURE_COPY_LOCATION* srcLocation,
const D3D12_BOX* srcBox) {
SkASSERT(fIsActive);
this->addingWork();
this->addResource(dst);
this->addResource(src);
fCommandList->CopyTextureRegion(dstLocation, dstX, dstY, 0, srcLocation, srcBox);
}
void GrD3DCommandList::addingWork() {
this->submitResourceBarriers();
fHasWork = true;

View File

@ -20,6 +20,10 @@ class GrD3DTextureResource;
class GrD3DCommandList {
public:
~GrD3DCommandList() {
this->releaseResources();
}
enum class SubmitResult {
kNoWork,
kSuccess,
@ -40,6 +44,19 @@ public:
int numBarriers,
D3D12_RESOURCE_TRANSITION_BARRIER* barriers);
// Helper method that calls copyTextureRegion multiple times, once for each subresource
void copyBufferToTexture(GrD3DBuffer* srcBuffer,
GrD3DTextureResource* dstTexture,
uint32_t subresourceCount,
D3D12_PLACED_SUBRESOURCE_FOOTPRINT* bufferFootprints,
int left, int top);
void copyTextureRegion(const GrManagedResource* dst,
const D3D12_TEXTURE_COPY_LOCATION* dstLocation,
UINT dstX, UINT dstY,
const GrManagedResource* src,
const D3D12_TEXTURE_COPY_LOCATION* srcLocation,
const D3D12_BOX* srcBox);
// Add ref-counted resource that will be tracked and released when this command buffer finishes
// execution
void addResource(const GrManagedResource* resource) {

View File

@ -71,7 +71,7 @@ GrD3DGpu::~GrD3DGpu() {
void GrD3DGpu::destroyResources() {
if (fCurrentDirectCommandList) {
fCurrentDirectCommandList->close();
fCurrentDirectCommandList.reset();
fCurrentDirectCommandList->reset();
}
// We need to make sure everything has finished on the queue.
@ -260,6 +260,195 @@ static bool check_resource_info(const GrD3DTextureResourceInfo& info) {
return true;
}
bool GrD3DGpu::onReadPixels(GrSurface* surface, int left, int top, int width, int height,
GrColorType surfaceColorType, GrColorType dstColorType, void* buffer,
size_t rowBytes) {
SkASSERT(surface);
if (surfaceColorType != dstColorType) {
return false;
}
// Set up src location and box
GrD3DTexture* d3dTex = static_cast<GrD3DTexture*>(surface->asTexture());
if (!d3dTex) {
return false;
}
D3D12_TEXTURE_COPY_LOCATION srcLocation = {};
srcLocation.pResource = d3dTex->d3dResource();
SkASSERT(srcLocation.pResource);
srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
srcLocation.SubresourceIndex = 0;
D3D12_BOX srcBox = {};
srcBox.left = left;
srcBox.top = top;
srcBox.right = left + width;
srcBox.bottom = top + height;
srcBox.front = 0;
srcBox.back = 1;
// Set up dst location and create transfer buffer
D3D12_TEXTURE_COPY_LOCATION dstLocation = {};
dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
UINT transferNumRows;
UINT64 transferRowBytes;
UINT64 transferTotalBytes;
const UINT64 baseOffset = 0;
D3D12_RESOURCE_DESC desc = srcLocation.pResource->GetDesc();
fDevice->GetCopyableFootprints(&desc, 0, 1, baseOffset, &dstLocation.PlacedFootprint,
&transferNumRows, &transferRowBytes, &transferTotalBytes);
SkASSERT(transferTotalBytes);
// TODO: implement some way of reusing buffers instead of making a new one every time.
sk_sp<GrGpuBuffer> transferBuffer = this->createBuffer(transferTotalBytes,
GrGpuBufferType::kXferGpuToCpu,
kDynamic_GrAccessPattern);
GrD3DBuffer* d3dBuf = static_cast<GrD3DBuffer*>(transferBuffer.get());
dstLocation.pResource = d3dBuf->d3dResource();
// Need to change the resource state to COPY_SOURCE in order to download from it
d3dTex->setResourceState(this, D3D12_RESOURCE_STATE_COPY_SOURCE);
fCurrentDirectCommandList->copyTextureRegion(d3dBuf->resource(), &dstLocation, 0, 0,
d3dTex->resource(), &srcLocation, &srcBox);
this->submitDirectCommandList(SyncQueue::kForce);
const void* mappedMemory = transferBuffer->map();
SkRectMemcpy(buffer, rowBytes, mappedMemory, dstLocation.PlacedFootprint.Footprint.RowPitch,
transferRowBytes, transferNumRows);
transferBuffer->unmap();
return true;
}
bool GrD3DGpu::onWritePixels(GrSurface* surface, int left, int top, int width, int height,
GrColorType surfaceColorType, GrColorType srcColorType,
const GrMipLevel texels[], int mipLevelCount,
bool prepForTexSampling) {
GrD3DTexture* d3dTex = static_cast<GrD3DTexture*>(surface->asTexture());
if (!d3dTex) {
return false;
}
// Make sure we have at least the base level
if (!mipLevelCount || !texels[0].fPixels) {
return false;
}
SkASSERT(!GrDxgiFormatIsCompressed(d3dTex->dxgiFormat()));
bool success = false;
// Need to change the resource state to COPY_DEST in order to upload to it
d3dTex->setResourceState(this, D3D12_RESOURCE_STATE_COPY_DEST);
SkASSERT(mipLevelCount <= d3dTex->texturePriv().maxMipMapLevel() + 1);
success = this->uploadToTexture(d3dTex, left, top, width, height, srcColorType, texels,
mipLevelCount);
if (prepForTexSampling) {
d3dTex->setResourceState(this, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
}
return success;
}
bool GrD3DGpu::uploadToTexture(GrD3DTexture* tex, int left, int top, int width, int height,
GrColorType colorType, const GrMipLevel* texels, int mipLevelCount) {
SkASSERT(this->caps()->isFormatTexturable(tex->backendFormat()));
// The assumption is either that we have no mipmaps, or that our rect is the entire texture
SkASSERT(1 == mipLevelCount ||
(0 == left && 0 == top && width == tex->width() && height == tex->height()));
// We assume that if the texture has mip levels, we either upload to all the levels or just the
// first.
SkASSERT(1 == mipLevelCount || mipLevelCount == (tex->texturePriv().maxMipMapLevel() + 1));
if (width == 0 || height == 0) {
return false;
}
SkASSERT(this->d3dCaps().surfaceSupportsWritePixels(tex));
SkASSERT(this->d3dCaps().areColorTypeAndFormatCompatible(colorType, tex->backendFormat()));
ID3D12Resource* d3dResource = tex->d3dResource();
SkASSERT(d3dResource);
D3D12_RESOURCE_DESC desc = d3dResource->GetDesc();
// Either upload only the first miplevel or all miplevels
SkASSERT(1 == mipLevelCount || mipLevelCount == (int)desc.MipLevels);
if (1 == mipLevelCount && !texels[0].fPixels) {
return true; // no data to upload
}
for (int i = 0; i < mipLevelCount; ++i) {
// We do not allow any gaps in the mip data
if (!texels[i].fPixels) {
return false;
}
}
SkAutoTMalloc<D3D12_PLACED_SUBRESOURCE_FOOTPRINT> placedFootprints(mipLevelCount);
SkAutoTMalloc<UINT> numRows(mipLevelCount);
SkAutoTMalloc<UINT64> rowBytes(mipLevelCount);
UINT64 combinedBufferSize;
// We reset the width and height in the description to match our subrectangle size
// so we don't end up allocating more space than we need.
desc.Width = width;
desc.Height = height;
fDevice->GetCopyableFootprints(&desc, 0, mipLevelCount, 0, placedFootprints.get(),
numRows.get(), rowBytes.get(), &combinedBufferSize);
SkASSERT(combinedBufferSize);
// TODO: do this until we have slices of buttery buffers
sk_sp<GrGpuBuffer> transferBuffer = this->createBuffer(combinedBufferSize,
GrGpuBufferType::kXferCpuToGpu,
kDynamic_GrAccessPattern);
if (!transferBuffer) {
return false;
}
char* bufferData = (char*)transferBuffer->map();
int currentWidth = width;
int currentHeight = height;
int layerHeight = tex->height();
for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
if (texels[currentMipLevel].fPixels) {
SkASSERT(1 == mipLevelCount || currentHeight == layerHeight);
const size_t trimRowBytes = rowBytes[currentMipLevel];
const size_t srcRowBytes = texels[currentMipLevel].fRowBytes;
char* dst = bufferData + placedFootprints[currentMipLevel].Offset;
// copy data into the buffer, skipping any trailing bytes
const char* src = (const char*)texels[currentMipLevel].fPixels;
SkASSERT(currentHeight == (int)numRows[currentMipLevel]);
SkRectMemcpy(dst, placedFootprints[currentMipLevel].Footprint.RowPitch,
src, srcRowBytes, trimRowBytes, currentHeight);
}
currentWidth = std::max(1, currentWidth / 2);
currentHeight = std::max(1, currentHeight / 2);
layerHeight = currentHeight;
}
transferBuffer->unmap();
GrD3DBuffer* d3dBuffer = static_cast<GrD3DBuffer*>(transferBuffer.get());
fCurrentDirectCommandList->copyBufferToTexture(d3dBuffer, tex, mipLevelCount,
placedFootprints.get(), left, top);
if (mipLevelCount < (int)desc.MipLevels) {
tex->texturePriv().markMipMapsDirty();
}
return true;
}
static bool check_tex_resource_info(const GrD3DCaps& caps, const GrD3DTextureResourceInfo& info) {
if (!caps.isFormatTexturable(info.fFormat)) {
return false;

View File

@ -145,16 +145,12 @@ private:
bool onReadPixels(GrSurface* surface, int left, int top, int width, int height,
GrColorType surfaceColorType, GrColorType dstColorType, void* buffer,
size_t rowBytes) override {
return true;
}
size_t rowBytes) override;
bool onWritePixels(GrSurface* surface, int left, int top, int width, int height,
GrColorType surfaceColorType, GrColorType srcColorType,
const GrMipLevel texels[], int mipLevelCount,
bool prepForTexSampling) override {
return true;
}
bool prepForTexSampling) override;
bool onTransferPixelsTo(GrTexture* texture, int left, int top, int width, int height,
GrColorType surfaceColorType, GrColorType bufferColorType,
@ -201,6 +197,9 @@ private:
void checkForFinishedCommandLists();
void waitForQueueCompletion();
bool uploadToTexture(GrD3DTexture* tex, int left, int top, int width, int height,
GrColorType colorType, const GrMipLevel* texels, int mipLevelCount);
bool createTextureResourceForBackendSurface(DXGI_FORMAT dxgiFormat,
SkISize dimensions,
GrTexturable texturable,