blob: 230c4c9de43a98c670005162831fe59d87ec0e8b [file] [log] [blame]
/*
* Copyright 2020 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/d3d/GrD3DTexture.h"
#include "src/gpu/GrTexture.h"
#include "src/gpu/d3d/GrD3DGpu.h"
#include "src/gpu/d3d/GrD3DUtil.h"
#include "include/gpu/d3d/GrD3DTypes.h"
// Because this class is virtually derived from GrSurface we must explicitly call its constructor.
GrD3DTexture::GrD3DTexture(GrD3DGpu* gpu,
SkBudgeted budgeted,
SkISize dimensions,
const GrD3DTextureResourceInfo& info,
sk_sp<GrD3DResourceState> state,
const GrD3DDescriptorHeap::CPUHandle& shaderResourceView,
GrMipmapStatus mipmapStatus)
: GrSurface(gpu, dimensions, info.fProtected)
, GrD3DTextureResource(info, std::move(state))
, INHERITED(gpu, dimensions, info.fProtected, GrTextureType::k2D, mipmapStatus)
, fShaderResourceView(shaderResourceView) {
SkASSERT((GrMipmapStatus::kNotAllocated == mipmapStatus) == (1 == info.fLevelCount));
this->registerWithCache(budgeted);
if (GrDxgiFormatIsCompressed(info.fFormat)) {
this->setReadOnly();
}
}
GrD3DTexture::GrD3DTexture(GrD3DGpu* gpu, SkISize dimensions, const GrD3DTextureResourceInfo& info,
sk_sp<GrD3DResourceState> state,
const GrD3DDescriptorHeap::CPUHandle& shaderResourceView,
GrMipmapStatus mipmapStatus, GrWrapCacheable cacheable,
GrIOType ioType)
: GrSurface(gpu, dimensions, info.fProtected)
, GrD3DTextureResource(info, std::move(state))
, INHERITED(gpu, dimensions, info.fProtected, GrTextureType::k2D, mipmapStatus)
, fShaderResourceView(shaderResourceView) {
SkASSERT((GrMipmapStatus::kNotAllocated == mipmapStatus) == (1 == info.fLevelCount));
if (ioType == kRead_GrIOType) {
this->setReadOnly();
}
this->registerWithCacheWrapped(cacheable);
}
// Because this class is virtually derived from GrSurface we must explicitly call its constructor.
GrD3DTexture::GrD3DTexture(GrD3DGpu* gpu,
SkISize dimensions,
const GrD3DTextureResourceInfo& info,
sk_sp<GrD3DResourceState> state,
const GrD3DDescriptorHeap::CPUHandle& shaderResourceView,
GrMipmapStatus mipmapStatus)
: GrSurface(gpu, dimensions, info.fProtected)
, GrD3DTextureResource(info, state)
, INHERITED(gpu, dimensions, info.fProtected, GrTextureType::k2D, mipmapStatus)
, fShaderResourceView(shaderResourceView) {
SkASSERT((GrMipmapStatus::kNotAllocated == mipmapStatus) == (1 == info.fLevelCount));
}
sk_sp<GrD3DTexture> GrD3DTexture::MakeNewTexture(GrD3DGpu* gpu, SkBudgeted budgeted,
SkISize dimensions,
const D3D12_RESOURCE_DESC& desc,
GrProtected isProtected,
GrMipmapStatus mipmapStatus) {
GrD3DTextureResourceInfo info;
if (!GrD3DTextureResource::InitTextureResourceInfo(gpu, desc,
D3D12_RESOURCE_STATE_COPY_DEST,
isProtected, nullptr, &info)) {
return nullptr;
}
sk_sp<GrD3DResourceState> state(
new GrD3DResourceState(static_cast<D3D12_RESOURCE_STATES>(info.fResourceState)));
GrD3DDescriptorHeap::CPUHandle shaderResourceView =
gpu->resourceProvider().createShaderResourceView(info.fResource.get());
GrD3DTexture* tex = new GrD3DTexture(gpu, budgeted, dimensions, info, std::move(state),
shaderResourceView, mipmapStatus);
return sk_sp<GrD3DTexture>(tex);
}
sk_sp<GrD3DTexture> GrD3DTexture::MakeWrappedTexture(GrD3DGpu* gpu,
SkISize dimensions,
GrWrapCacheable cacheable,
GrIOType ioType,
const GrD3DTextureResourceInfo& info,
sk_sp<GrD3DResourceState> state) {
// TODO: If a client uses their own heap to allocate, how do we manage that?
// Adopted textures require both image and allocation because we're responsible for freeing
//SkASSERT(info.fTexture &&
// (kBorrow_GrWrapOwnership == wrapOwnership || VK_NULL_HANDLE != info.fAlloc.fMemory));
GrMipmapStatus mipmapStatus = info.fLevelCount > 1 ? GrMipmapStatus::kValid
: GrMipmapStatus::kNotAllocated;
GrD3DDescriptorHeap::CPUHandle shaderResourceView =
gpu->resourceProvider().createShaderResourceView(info.fResource.get());
return sk_sp<GrD3DTexture>(new GrD3DTexture(gpu, dimensions, info, std::move(state),
shaderResourceView, mipmapStatus, cacheable,
ioType));
}
void GrD3DTexture::onRelease() {
// We're about to be severed from our GrManagedResource. If there are "finish" idle procs we
// have to decide who will handle them. If the resource is still tied to a command buffer we let
// it handle them. Otherwise, we handle them.
SkASSERT(this->resource());
if (this->resource()->isQueuedForWorkOnGpu()) {
this->removeFinishIdleProcs();
}
GrD3DGpu* gpu = this->getD3DGpu();
gpu->resourceProvider().recycleConstantOrShaderView(fShaderResourceView);
this->releaseResource(gpu);
INHERITED::onRelease();
}
void GrD3DTexture::onAbandon() {
// We're about to be severed from our GrManagedResource. If there are "finish" idle procs we
// have to decide who will handle them. If the resource is still tied to a command buffer we let
// it handle them. Otherwise, we handle them.
SkASSERT(this->resource());
if (this->resource()->isQueuedForWorkOnGpu()) {
this->removeFinishIdleProcs();
}
GrD3DGpu* gpu = this->getD3DGpu();
gpu->resourceProvider().recycleConstantOrShaderView(fShaderResourceView);
this->releaseResource(gpu);
INHERITED::onAbandon();
}
GrBackendTexture GrD3DTexture::getBackendTexture() const {
return GrBackendTexture(this->width(), this->height(), fInfo, this->grD3DResourceState());
}
GrD3DGpu* GrD3DTexture::getD3DGpu() const {
SkASSERT(!this->wasDestroyed());
return static_cast<GrD3DGpu*>(this->getGpu());
}
void GrD3DTexture::addIdleProc(sk_sp<GrRefCntedCallback> idleProc) {
INHERITED::addIdleProc(idleProc);
this->addResourceIdleProc(this, std::move(idleProc));
}
void GrD3DTexture::callIdleProcsOnBehalfOfResource() {
// If we got here then the resource is being removed from its last command buffer and the
// texture is idle in the cache. Any kFlush idle procs should already have been called. So
// the texture and resource should have the same set of procs.
SkASSERT(this->resourceIdleProcCnt() == fIdleProcs.count());
#ifdef SK_DEBUG
for (int i = 0; i < fIdleProcs.count(); ++i) {
SkASSERT(fIdleProcs[i] == this->resourceIdleProc(i));
}
#endif
fIdleProcs.reset();
this->resetResourceIdleProcs();
}
void GrD3DTexture::willRemoveLastRef() {
if (!fIdleProcs.count()) {
return;
}
// This is called when the GrTexture is purgeable. However, we need to check whether the
// Resource is still owned by any command buffers. If it is then it will call the proc.
if (!this->resourceIsQueuedForWorkOnGpu()) {
// Everything must go!
fIdleProcs.reset();
this->resetResourceIdleProcs();
} else {
// The procs that should be called on flush but not finish are those that are owned
// by the GrD3DTexture and not the Resource. We do this by copying the resource's array
// and thereby dropping refs to procs we own but the resource does not.
fIdleProcs.reset(this->resourceIdleProcCnt());
for (int i = 0; i < fIdleProcs.count(); ++i) {
fIdleProcs[i] = this->resourceIdleProc(i);
}
}
}
void GrD3DTexture::removeFinishIdleProcs() {
// This should only be called by onRelease/onAbandon when we have already checked for a
// resource.
SkSTArray<4, sk_sp<GrRefCntedCallback>> procsToKeep;
int resourceIdx = 0;
// The idle procs that are common between the GrD3DTexture and its Resource should be found in
// the same order.
for (int i = 0; i < fIdleProcs.count(); ++i) {
if (fIdleProcs[i] == this->resourceIdleProc(resourceIdx)) {
++resourceIdx;
} else {
procsToKeep.push_back(fIdleProcs[i]);
}
}
SkASSERT(resourceIdx == this->resourceIdleProcCnt());
fIdleProcs = procsToKeep;
}