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:
parent
1ba00c7121
commit
ba7f22954d
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user