blob: 9e64a054dc6558dda207876a03fd433367d9c188 [file] [log] [blame]
/*
* Copyright 2019 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/dawn/GrDawnGpu.h"
#include "include/gpu/GrBackendSemaphore.h"
#include "include/gpu/GrBackendSurface.h"
#include "include/gpu/GrContextOptions.h"
#include "include/gpu/GrDirectContext.h"
#include "src/core/SkConvertPixels.h"
#include "src/gpu/GrDataUtils.h"
#include "src/gpu/GrDirectContextPriv.h"
#include "src/gpu/GrGeometryProcessor.h"
#include "src/gpu/GrGpuResourceCacheAccess.h"
#include "src/gpu/GrPipeline.h"
#include "src/gpu/GrRenderTarget.h"
#include "src/gpu/GrSemaphore.h"
#include "src/gpu/GrStencilSettings.h"
#include "src/gpu/GrTexture.h"
#include "src/gpu/dawn/GrDawnAttachment.h"
#include "src/gpu/dawn/GrDawnBuffer.h"
#include "src/gpu/dawn/GrDawnCaps.h"
#include "src/gpu/dawn/GrDawnOpsRenderPass.h"
#include "src/gpu/dawn/GrDawnProgramBuilder.h"
#include "src/gpu/dawn/GrDawnRenderTarget.h"
#include "src/gpu/dawn/GrDawnTexture.h"
#include "src/gpu/dawn/GrDawnUtil.h"
#include "src/core/SkAutoMalloc.h"
#include "src/core/SkMipmap.h"
#include "src/sksl/SkSLCompiler.h"
#if !defined(SK_BUILD_FOR_WIN)
#include <unistd.h>
#endif // !defined(SK_BUILD_FOR_WIN)
static const int kMaxRenderPipelineEntries = 1024;
namespace {
class Fence {
public:
Fence(const wgpu::Device& device, const wgpu::Fence& fence)
: fDevice(device), fFence(fence), fCalled(false) {
fFence.OnCompletion(0, callback, this);
}
static void callback(WGPUFenceCompletionStatus status, void* userData) {
Fence* fence = static_cast<Fence*>(userData);
fence->fCalled = true;
}
bool check() {
fDevice.Tick();
return fCalled;
}
wgpu::Fence fence() { return fFence; }
private:
wgpu::Device fDevice;
wgpu::Fence fFence;
bool fCalled;
};
}
static wgpu::FilterMode to_dawn_filter_mode(GrSamplerState::Filter filter) {
switch (filter) {
case GrSamplerState::Filter::kNearest:
return wgpu::FilterMode::Nearest;
case GrSamplerState::Filter::kLinear:
return wgpu::FilterMode::Linear;
default:
SkASSERT(!"unsupported filter mode");
return wgpu::FilterMode::Nearest;
}
}
static wgpu::FilterMode to_dawn_mipmap_mode(GrSamplerState::MipmapMode mode) {
switch (mode) {
case GrSamplerState::MipmapMode::kNone:
// Fall-through (Dawn does not have an equivalent for "None")
case GrSamplerState::MipmapMode::kNearest:
return wgpu::FilterMode::Nearest;
case GrSamplerState::MipmapMode::kLinear:
return wgpu::FilterMode::Linear;
default:
SkASSERT(!"unsupported filter mode");
return wgpu::FilterMode::Nearest;
}
}
static wgpu::AddressMode to_dawn_address_mode(GrSamplerState::WrapMode wrapMode) {
switch (wrapMode) {
case GrSamplerState::WrapMode::kClamp:
return wgpu::AddressMode::ClampToEdge;
case GrSamplerState::WrapMode::kRepeat:
return wgpu::AddressMode::Repeat;
case GrSamplerState::WrapMode::kMirrorRepeat:
return wgpu::AddressMode::MirrorRepeat;
case GrSamplerState::WrapMode::kClampToBorder:
SkASSERT(!"unsupported address mode");
}
SkASSERT(!"unsupported address mode");
return wgpu::AddressMode::ClampToEdge;
}
sk_sp<GrGpu> GrDawnGpu::Make(const wgpu::Device& device,
const GrContextOptions& options, GrDirectContext* direct) {
if (!device) {
return nullptr;
}
return sk_sp<GrGpu>(new GrDawnGpu(direct, options, device));
}
////////////////////////////////////////////////////////////////////////////////
GrDawnGpu::GrDawnGpu(GrDirectContext* direct, const GrContextOptions& options,
const wgpu::Device& device)
: INHERITED(direct)
, fDevice(device)
, fQueue(device.GetDefaultQueue())
, fUniformRingBuffer(this, wgpu::BufferUsage::Uniform)
, fStagingBufferManager(this)
, fRenderPipelineCache(kMaxRenderPipelineEntries)
, fFinishCallbacks(this) {
this->initCapsAndCompiler(sk_make_sp<GrDawnCaps>(options));
}
GrDawnGpu::~GrDawnGpu() { this->finishOutstandingGpuWork(); }
void GrDawnGpu::disconnect(DisconnectType type) {
if (DisconnectType::kCleanup == type) {
this->finishOutstandingGpuWork();
}
fStagingBufferManager.reset();
fQueue = nullptr;
fDevice = nullptr;
INHERITED::disconnect(type);
}
///////////////////////////////////////////////////////////////////////////////
GrOpsRenderPass* GrDawnGpu::onGetOpsRenderPass(
GrRenderTarget* rt,
GrAttachment*,
GrSurfaceOrigin origin,
const SkIRect& bounds,
const GrOpsRenderPass::LoadAndStoreInfo& colorInfo,
const GrOpsRenderPass::StencilLoadAndStoreInfo& stencilInfo,
const SkTArray<GrSurfaceProxy*, true>& sampledProxies,
GrXferBarrierFlags renderPassXferBarriers) {
fOpsRenderPass.reset(new GrDawnOpsRenderPass(this, rt, origin, colorInfo, stencilInfo));
return fOpsRenderPass.get();
}
///////////////////////////////////////////////////////////////////////////////
sk_sp<GrGpuBuffer> GrDawnGpu::onCreateBuffer(size_t size, GrGpuBufferType type,
GrAccessPattern accessPattern, const void* data) {
sk_sp<GrGpuBuffer> b(new GrDawnBuffer(this, size, type, accessPattern));
if (data && b) {
b->updateData(data, size);
}
return b;
}
////////////////////////////////////////////////////////////////////////////////
bool GrDawnGpu::onWritePixels(GrSurface* surface, int left, int top, int width, int height,
GrColorType surfaceColorType, GrColorType srcColorType,
const GrMipLevel texels[], int mipLevelCount,
bool prepForTexSampling) {
GrDawnTexture* texture = static_cast<GrDawnTexture*>(surface->asTexture());
if (!texture) {
return false;
}
this->uploadTextureData(srcColorType, texels, mipLevelCount,
SkIRect::MakeXYWH(left, top, width, height), texture->texture());
if (mipLevelCount < texture->maxMipmapLevel() + 1) {
texture->markMipmapsDirty();
}
return true;
}
bool GrDawnGpu::onTransferPixelsTo(GrTexture* texture, int left, int top, int width, int height,
GrColorType textureColorType, GrColorType bufferColorType,
sk_sp<GrGpuBuffer> transferBuffer, size_t bufferOffset,
size_t rowBytes) {
SkASSERT(!"unimplemented");
return false;
}
bool GrDawnGpu::onTransferPixelsFrom(GrSurface* surface, int left, int top, int width, int height,
GrColorType surfaceColorType, GrColorType bufferColorType,
sk_sp<GrGpuBuffer> transferBuffer, size_t offset) {
SkASSERT(!"unimplemented");
return false;
}
////////////////////////////////////////////////////////////////////////////////
sk_sp<GrTexture> GrDawnGpu::onCreateTexture(SkISize dimensions,
const GrBackendFormat& backendFormat,
GrRenderable renderable,
int renderTargetSampleCnt,
SkBudgeted budgeted,
GrProtected,
int mipLevelCount,
uint32_t levelClearMask) {
if (levelClearMask) {
return nullptr;
}
wgpu::TextureFormat format;
if (!backendFormat.asDawnFormat(&format)) {
return nullptr;
}
GrMipmapStatus mipmapStatus =
mipLevelCount > 1 ? GrMipmapStatus::kDirty : GrMipmapStatus::kNotAllocated;
return GrDawnTexture::Make(this, dimensions, format, renderable, renderTargetSampleCnt,
budgeted, mipLevelCount, mipmapStatus);
}
sk_sp<GrTexture> GrDawnGpu::onCreateCompressedTexture(SkISize dimensions, const GrBackendFormat&,
SkBudgeted, GrMipmapped, GrProtected,
const void* data, size_t dataSize) {
SkASSERT(!"unimplemented");
return nullptr;
}
sk_sp<GrTexture> GrDawnGpu::onWrapBackendTexture(const GrBackendTexture& backendTex,
GrWrapOwnership ownership,
GrWrapCacheable cacheable,
GrIOType ioType) {
GrDawnTextureInfo info;
if (!backendTex.getDawnTextureInfo(&info)) {
return nullptr;
}
SkISize dimensions = { backendTex.width(), backendTex.height() };
return GrDawnTexture::MakeWrapped(this, dimensions, GrRenderable::kNo, 1, cacheable, ioType,
info);
}
sk_sp<GrTexture> GrDawnGpu::onWrapCompressedBackendTexture(const GrBackendTexture& backendTex,
GrWrapOwnership ownership,
GrWrapCacheable cacheable) {
return nullptr;
}
sk_sp<GrTexture> GrDawnGpu::onWrapRenderableBackendTexture(const GrBackendTexture& tex,
int sampleCnt,
GrWrapOwnership,
GrWrapCacheable cacheable) {
GrDawnTextureInfo info;
if (!tex.getDawnTextureInfo(&info) || !info.fTexture) {
return nullptr;
}
SkISize dimensions = { tex.width(), tex.height() };
sampleCnt = this->caps()->getRenderTargetSampleCount(sampleCnt, tex.getBackendFormat());
if (sampleCnt < 1) {
return nullptr;
}
sk_sp<GrTexture> result = GrDawnTexture::MakeWrapped(this, dimensions, GrRenderable::kYes,
sampleCnt, cacheable, kRW_GrIOType, info);
result->markMipmapsDirty();
return result;
}
sk_sp<GrRenderTarget> GrDawnGpu::onWrapBackendRenderTarget(const GrBackendRenderTarget& rt) {
GrDawnRenderTargetInfo info;
if (!rt.getDawnRenderTargetInfo(&info) || !info.fTextureView) {
return nullptr;
}
SkISize dimensions = { rt.width(), rt.height() };
int sampleCnt = 1;
return GrDawnRenderTarget::MakeWrapped(this, dimensions, sampleCnt, info);
}
sk_sp<GrAttachment> GrDawnGpu::makeStencilAttachmentForRenderTarget(const GrRenderTarget* rt,
SkISize dimensions,
int numStencilSamples) {
fStats.incStencilAttachmentCreates();
return GrDawnAttachment::MakeStencil(this, dimensions, numStencilSamples);
}
GrBackendTexture GrDawnGpu::onCreateBackendTexture(SkISize dimensions,
const GrBackendFormat& backendFormat,
GrRenderable renderable,
GrMipmapped mipMapped,
GrProtected isProtected) {
wgpu::TextureFormat format;
if (!backendFormat.asDawnFormat(&format)) {
return GrBackendTexture();
}
wgpu::TextureDescriptor desc;
desc.usage =
wgpu::TextureUsage::Sampled |
wgpu::TextureUsage::CopySrc |
wgpu::TextureUsage::CopyDst;
if (GrRenderable::kYes == renderable) {
desc.usage |= wgpu::TextureUsage::OutputAttachment;
}
int numMipLevels = 1;
if (mipMapped == GrMipmapped::kYes) {
numMipLevels = SkMipmap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
}
desc.size.width = dimensions.width();
desc.size.height = dimensions.height();
desc.size.depth = 1;
desc.format = format;
desc.mipLevelCount = numMipLevels;
wgpu::Texture tex = this->device().CreateTexture(&desc);
GrDawnTextureInfo info;
info.fTexture = tex;
info.fFormat = desc.format;
info.fLevelCount = desc.mipLevelCount;
return GrBackendTexture(dimensions.width(), dimensions.height(), info);
}
void GrDawnGpu::uploadTextureData(GrColorType srcColorType, const GrMipLevel texels[],
int mipLevelCount, const SkIRect& rect,
wgpu::Texture texture) {
uint32_t x = rect.x();
uint32_t y = rect.y();
uint32_t width = rect.width();
uint32_t height = rect.height();
for (int i = 0; i < mipLevelCount; i++) {
const void* src = texels[i].fPixels;
size_t srcRowBytes = texels[i].fRowBytes;
SkColorType colorType = GrColorTypeToSkColorType(srcColorType);
size_t trimRowBytes = width * SkColorTypeBytesPerPixel(colorType);
size_t dstRowBytes = GrDawnRoundRowBytes(trimRowBytes);
size_t size = dstRowBytes * height;
GrStagingBufferManager::Slice slice =
this->stagingBufferManager()->allocateStagingBufferSlice(size);
SkRectMemcpy(slice.fOffsetMapPtr, dstRowBytes, src, srcRowBytes, trimRowBytes, height);
wgpu::BufferCopyView srcBuffer = {};
srcBuffer.buffer = static_cast<GrDawnBuffer*>(slice.fBuffer)->get();
srcBuffer.layout.offset = slice.fOffset;
srcBuffer.layout.bytesPerRow = dstRowBytes;
srcBuffer.layout.rowsPerImage = height;
wgpu::TextureCopyView dstTexture;
dstTexture.texture = texture;
dstTexture.mipLevel = i;
dstTexture.origin = {x, y, 0};
wgpu::Extent3D copySize = {width, height, 1};
this->getCopyEncoder().CopyBufferToTexture(&srcBuffer, &dstTexture, &copySize);
x /= 2;
y /= 2;
width = std::max(1u, width / 2);
height = std::max(1u, height / 2);
}
}
bool GrDawnGpu::onUpdateBackendTexture(const GrBackendTexture& backendTexture,
sk_sp<GrRefCntedCallback> finishedCallback,
const BackendTextureData* data) {
GrDawnTextureInfo info;
SkAssertResult(backendTexture.getDawnTextureInfo(&info));
size_t bpp = GrDawnBytesPerBlock(info.fFormat);
size_t baseLayerSize = bpp * backendTexture.width() * backendTexture.height();
const void* pixels;
SkAutoMalloc defaultStorage(baseLayerSize);
if (data && data->type() == BackendTextureData::Type::kPixmaps) {
SkTDArray<GrMipLevel> texels;
GrColorType colorType = data->pixmap(0).colorType();
int numMipLevels = info.fLevelCount;
texels.append(numMipLevels);
for (int i = 0; i < numMipLevels; ++i) {
texels[i] = {data->pixmap(i).addr(), data->pixmap(i).rowBytes()};
}
SkIRect dstRect = SkIRect::MakeSize(backendTexture.dimensions());
this->uploadTextureData(colorType, texels.begin(), texels.count(), dstRect, info.fTexture);
return true;
}
pixels = defaultStorage.get();
GrColorType colorType;
if (!GrDawnFormatToGrColorType(info.fFormat, &colorType)) {
return false;
}
SkISize size{backendTexture.width(), backendTexture.height()};
GrImageInfo imageInfo(colorType, kUnpremul_SkAlphaType, nullptr, size);
GrClearImage(imageInfo, defaultStorage.get(), bpp * backendTexture.width(), data->color());
wgpu::Device device = this->device();
wgpu::CommandEncoder copyEncoder = this->getCopyEncoder();
int w = backendTexture.width(), h = backendTexture.height();
for (uint32_t i = 0; i < info.fLevelCount; i++) {
size_t origRowBytes = bpp * w;
size_t rowBytes = GrDawnRoundRowBytes(origRowBytes);
size_t size = rowBytes * h;
GrStagingBufferManager::Slice stagingBuffer =
this->stagingBufferManager()->allocateStagingBufferSlice(size);
if (rowBytes == origRowBytes) {
memcpy(stagingBuffer.fOffsetMapPtr, pixels, size);
} else {
const char* src = static_cast<const char*>(pixels);
char* dst = static_cast<char*>(stagingBuffer.fOffsetMapPtr);
for (int row = 0; row < h; row++) {
memcpy(dst, src, origRowBytes);
dst += rowBytes;
src += origRowBytes;
}
}
wgpu::BufferCopyView srcBuffer = {};
srcBuffer.buffer = static_cast<GrDawnBuffer*>(stagingBuffer.fBuffer)->get();
srcBuffer.layout.offset = stagingBuffer.fOffset;
srcBuffer.layout.bytesPerRow = rowBytes;
srcBuffer.layout.rowsPerImage = h;
wgpu::TextureCopyView dstTexture;
dstTexture.texture = info.fTexture;
dstTexture.mipLevel = i;
dstTexture.origin = {0, 0, 0};
wgpu::Extent3D copySize = {(uint32_t)w, (uint32_t)h, 1};
copyEncoder.CopyBufferToTexture(&srcBuffer, &dstTexture, &copySize);
w = std::max(1, w / 2);
h = std::max(1, h / 2);
}
return true;
}
GrBackendTexture GrDawnGpu::onCreateCompressedBackendTexture(
SkISize dimensions, const GrBackendFormat&, GrMipmapped, GrProtected) {
return {};
}
bool GrDawnGpu::onUpdateCompressedBackendTexture(const GrBackendTexture&,
sk_sp<GrRefCntedCallback> finishedCallback,
const BackendTextureData*) {
return false;
}
void GrDawnGpu::deleteBackendTexture(const GrBackendTexture& tex) {
GrDawnTextureInfo info;
if (tex.getDawnTextureInfo(&info)) {
info.fTexture = nullptr;
}
}
bool GrDawnGpu::compile(const GrProgramDesc&, const GrProgramInfo&) {
return false;
}
#if GR_TEST_UTILS
bool GrDawnGpu::isTestingOnlyBackendTexture(const GrBackendTexture& tex) const {
GrDawnTextureInfo info;
if (!tex.getDawnTextureInfo(&info)) {
return false;
}
return info.fTexture.Get();
}
GrBackendRenderTarget GrDawnGpu::createTestingOnlyBackendRenderTarget(SkISize dimensions,
GrColorType colorType,
int sampleCnt,
GrProtected isProtected) {
if (dimensions.width() > this->caps()->maxTextureSize() ||
dimensions.height() > this->caps()->maxTextureSize()) {
return {};
}
// We don't support MSAA in this backend yet.
if (sampleCnt != 1) {
return {};
}
if (isProtected == GrProtected::kYes) {
return {};
}
wgpu::TextureFormat format;
if (!GrColorTypeToDawnFormat(colorType, &format)) {
return {};
}
wgpu::TextureDescriptor desc;
desc.usage =
wgpu::TextureUsage::CopySrc |
wgpu::TextureUsage::OutputAttachment;
desc.size.width = dimensions.width();
desc.size.height = dimensions.height();
desc.size.depth = 1;
desc.format = format;
wgpu::Texture tex = this->device().CreateTexture(&desc);
GrDawnRenderTargetInfo info;
info.fTextureView = tex.CreateView();
info.fFormat = desc.format;
info.fLevelCount = desc.mipLevelCount;
return GrBackendRenderTarget(dimensions.width(), dimensions.height(), 1, 0, info);
}
void GrDawnGpu::deleteTestingOnlyBackendRenderTarget(const GrBackendRenderTarget& rt) {
GrDawnRenderTargetInfo info;
if (rt.getDawnRenderTargetInfo(&info)) {
info.fTextureView = nullptr;
}
}
#endif
void GrDawnGpu::addFinishedProc(GrGpuFinishedProc finishedProc,
GrGpuFinishedContext finishedContext) {
fFinishCallbacks.add(finishedProc, finishedContext);
}
void GrDawnGpu::checkForCompletedStagingBuffers() {
// We expect all the buffer maps to trigger in order of submission so we bail after the first
// non finished map since we always push new busy buffers to the back of our list.
while (!fBusyStagingBuffers.empty() && fBusyStagingBuffers.front()->isMapped()) {
fBusyStagingBuffers.pop_front();
}
}
void GrDawnGpu::waitOnAllBusyStagingBuffers() {
while (!fBusyStagingBuffers.empty()) {
fDevice.Tick();
this->checkForCompletedStagingBuffers();
}
}
void GrDawnGpu::takeOwnershipOfBuffer(sk_sp<GrGpuBuffer> buffer) {
fSubmittedStagingBuffers.push_back(std::move(buffer));
}
static void callback(WGPUFenceCompletionStatus status, void* userData) {
*static_cast<bool*>(userData) = true;
}
bool GrDawnGpu::onSubmitToGpu(bool syncCpu) {
this->flushCopyEncoder();
if (!fCommandBuffers.empty()) {
fQueue.Submit(fCommandBuffers.size(), &fCommandBuffers.front());
fCommandBuffers.clear();
}
this->moveStagingBuffersToBusyAndMapAsync();
if (syncCpu) {
wgpu::FenceDescriptor desc;
wgpu::Fence fence = fQueue.CreateFence(&desc);
bool called = false;
fence.OnCompletion(0, callback, &called);
while (!called) {
fDevice.Tick();
}
fFinishCallbacks.callAll(true);
}
this->checkForCompletedStagingBuffers();
return true;
}
static wgpu::Texture get_dawn_texture_from_surface(GrSurface* src) {
if (auto t = static_cast<GrDawnTexture*>(src->asTexture())) {
return t->texture();
} else {
return nullptr;
}
}
bool GrDawnGpu::onCopySurface(GrSurface* dst,
GrSurface* src,
const SkIRect& srcRect,
const SkIPoint& dstPoint) {
wgpu::Texture srcTexture = get_dawn_texture_from_surface(src);
wgpu::Texture dstTexture = get_dawn_texture_from_surface(dst);
if (!srcTexture || !dstTexture) {
return false;
}
uint32_t width = srcRect.width(), height = srcRect.height();
wgpu::TextureCopyView srcTextureView, dstTextureView;
srcTextureView.texture = srcTexture;
srcTextureView.origin = {(uint32_t) srcRect.x(), (uint32_t) srcRect.y(), 0};
dstTextureView.texture = dstTexture;
dstTextureView.origin = {(uint32_t) dstPoint.x(), (uint32_t) dstPoint.y(), 0};
wgpu::Extent3D copySize = {width, height, 1};
this->getCopyEncoder().CopyTextureToTexture(&srcTextureView, &dstTextureView, &copySize);
return true;
}
static void callback(WGPUBufferMapAsyncStatus status, void* userdata) {
*static_cast<bool*>(userdata) = true;
}
bool GrDawnGpu::onReadPixels(GrSurface* surface, int left, int top, int width, int height,
GrColorType surfaceColorType, GrColorType dstColorType, void* buffer,
size_t rowBytes) {
wgpu::Texture tex = get_dawn_texture_from_surface(surface);
if (!tex || 0 == rowBytes) {
return false;
}
size_t origRowBytes = rowBytes;
int origSizeInBytes = origRowBytes * height;
rowBytes = GrDawnRoundRowBytes(rowBytes);
int sizeInBytes = rowBytes * height;
wgpu::BufferDescriptor desc;
desc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::MapRead;
desc.size = sizeInBytes;
wgpu::Buffer buf = device().CreateBuffer(&desc);
wgpu::TextureCopyView srcTexture;
srcTexture.texture = tex;
srcTexture.origin = {(uint32_t) left, (uint32_t) top, 0};
wgpu::BufferCopyView dstBuffer = {};
dstBuffer.buffer = buf;
dstBuffer.layout.offset = 0;
dstBuffer.layout.bytesPerRow = rowBytes;
dstBuffer.layout.rowsPerImage = height;
wgpu::Extent3D copySize = {(uint32_t) width, (uint32_t) height, 1};
this->getCopyEncoder().CopyTextureToBuffer(&srcTexture, &dstBuffer, &copySize);
this->submitToGpu(true);
bool mapped = false;
buf.MapAsync(wgpu::MapMode::Read, 0, 0, callback, &mapped);
while (!mapped) {
device().Tick();
}
const void* readPixelsPtr = buf.GetConstMappedRange();
if (rowBytes == origRowBytes) {
memcpy(buffer, readPixelsPtr, origSizeInBytes);
} else {
const char* src = static_cast<const char*>(readPixelsPtr);
char* dst = static_cast<char*>(buffer);
for (int row = 0; row < height; row++) {
memcpy(dst, src, origRowBytes);
dst += origRowBytes;
src += rowBytes;
}
}
buf.Unmap();
return true;
}
bool GrDawnGpu::onRegenerateMipMapLevels(GrTexture* tex) {
this->flushCopyEncoder();
GrDawnTexture* src = static_cast<GrDawnTexture*>(tex);
int srcWidth = tex->width();
int srcHeight = tex->height();
// SkMipmap doesn't include the base level in the level count so we have to add 1
uint32_t levelCount = SkMipmap::ComputeLevelCount(tex->width(), tex->height()) + 1;
// Create a temporary texture for mipmap generation, then copy to source.
// We have to do this even for renderable textures, since GrDawnRenderTarget currently only
// contains a view, not a texture.
wgpu::TextureDescriptor texDesc;
texDesc.usage = wgpu::TextureUsage::Sampled |
wgpu::TextureUsage::CopySrc |
wgpu::TextureUsage::OutputAttachment;
texDesc.size.width = (tex->width() + 1) / 2;
texDesc.size.height = (tex->height() + 1) / 2;
texDesc.size.depth = 1;
texDesc.mipLevelCount = levelCount - 1;
texDesc.format = src->format();
wgpu::Texture dstTexture = fDevice.CreateTexture(&texDesc);
const char* vs =
"layout(location = 0) out float2 texCoord;\n"
"float2 positions[4] = float2[4](float2(-1.0, 1.0),\n"
"float2(1.0, 1.0),\n"
"float2(-1.0, -1.0),\n"
"float2(1.0, -1.0));\n"
"float2 texCoords[4] = float2[4](float2(0.0, 0.0),\n"
"float2(1.0, 0.0),\n"
"float2(0.0, 1.0),\n"
"float2(1.0, 1.0));\n"
"void main() {\n"
" sk_Position = float4(positions[sk_VertexID], 0.0, 1.0);\n"
" texCoord = texCoords[sk_VertexID];\n"
"}\n";
SkSL::String vsSPIRV =
this->SkSLToSPIRV(vs, SkSL::ProgramKind::kVertex, false, 0, nullptr);
const char* fs =
"layout(set = 0, binding = 0) uniform sampler samp;\n"
"layout(set = 0, binding = 1) uniform texture2D tex;\n"
"layout(location = 0) in float2 texCoord;\n"
"void main() {\n"
" sk_FragColor = sample(makeSampler2D(tex, samp), texCoord);\n"
"}\n";
SkSL::String fsSPIRV =
this->SkSLToSPIRV(fs, SkSL::ProgramKind::kFragment, false, 0, nullptr);
wgpu::ProgrammableStageDescriptor vsDesc;
vsDesc.module = this->createShaderModule(vsSPIRV);
vsDesc.entryPoint = "main";
wgpu::ProgrammableStageDescriptor fsDesc;
fsDesc.module = this->createShaderModule(fsSPIRV);
fsDesc.entryPoint = "main";
wgpu::VertexStateDescriptor vertexStateDesc;
vertexStateDesc.indexFormat = wgpu::IndexFormat::Uint32;
wgpu::ColorStateDescriptor csDesc;
csDesc.format = static_cast<GrDawnTexture*>(tex)->format();
wgpu::RenderPipelineDescriptor renderPipelineDesc;
renderPipelineDesc.vertexStage = vsDesc;
renderPipelineDesc.fragmentStage = &fsDesc;
renderPipelineDesc.vertexState = &vertexStateDesc;
renderPipelineDesc.primitiveTopology = wgpu::PrimitiveTopology::TriangleStrip;
renderPipelineDesc.colorStateCount = 1;
renderPipelineDesc.colorStates = &csDesc;
wgpu::RenderPipeline pipeline = fDevice.CreateRenderPipeline(&renderPipelineDesc);
wgpu::BindGroupLayout bgl = pipeline.GetBindGroupLayout(0);
wgpu::TextureViewDescriptor srcViewDesc;
srcViewDesc.mipLevelCount = 1;
wgpu::TextureView srcView = src->texture().CreateView(&srcViewDesc);
wgpu::SamplerDescriptor samplerDesc;
samplerDesc.minFilter = wgpu::FilterMode::Linear;
wgpu::Sampler sampler = fDevice.CreateSampler(&samplerDesc);
wgpu::CommandEncoder commandEncoder = fDevice.CreateCommandEncoder();
for (uint32_t mipLevel = 0; mipLevel < texDesc.mipLevelCount; mipLevel++) {
int dstWidth = std::max(1, srcWidth / 2);
int dstHeight = std::max(1, srcHeight / 2);
wgpu::TextureViewDescriptor dstViewDesc;
dstViewDesc.format = static_cast<GrDawnTexture*>(tex)->format();
dstViewDesc.dimension = wgpu::TextureViewDimension::e2D;
dstViewDesc.baseMipLevel = mipLevel;
dstViewDesc.mipLevelCount = 1;
wgpu::TextureView dstView = dstTexture.CreateView(&dstViewDesc);
wgpu::BindGroupEntry bge[2];
bge[0].binding = 0;
bge[0].sampler = sampler;
bge[1].binding = 1;
bge[1].textureView = srcView;
wgpu::BindGroupDescriptor bgDesc;
bgDesc.layout = bgl;
bgDesc.entryCount = 2;
bgDesc.entries = bge;
wgpu::BindGroup bindGroup = fDevice.CreateBindGroup(&bgDesc);
wgpu::RenderPassColorAttachmentDescriptor colorAttachment;
colorAttachment.attachment = dstView;
colorAttachment.clearColor = { 0.0f, 0.0f, 0.0f, 0.0f };
colorAttachment.loadOp = wgpu::LoadOp::Load;
colorAttachment.storeOp = wgpu::StoreOp::Store;
wgpu::RenderPassColorAttachmentDescriptor* colorAttachments = { &colorAttachment };
wgpu::RenderPassDescriptor renderPassDesc;
renderPassDesc.colorAttachmentCount = 1;
renderPassDesc.colorAttachments = colorAttachments;
wgpu::RenderPassEncoder rpe = commandEncoder.BeginRenderPass(&renderPassDesc);
rpe.SetPipeline(pipeline);
rpe.SetBindGroup(0, bindGroup);
rpe.Draw(4, 1, 0, 0);
rpe.EndPass();
wgpu::Extent3D copySize = {(uint32_t)dstWidth, (uint32_t)dstHeight, 1};
wgpu::TextureCopyView srcCopyView;
srcCopyView.texture = dstTexture;
srcCopyView.mipLevel = mipLevel;
wgpu::TextureCopyView dstCopyView;
dstCopyView.mipLevel = mipLevel + 1;
dstCopyView.texture = src->texture();
commandEncoder.CopyTextureToTexture(&srcCopyView, &dstCopyView, &copySize);
srcHeight = dstHeight;
srcWidth = dstWidth;
srcView = dstView;
}
fCommandBuffers.push_back(commandEncoder.Finish());
return true;
}
void GrDawnGpu::submit(GrOpsRenderPass* renderPass) {
this->flushCopyEncoder();
static_cast<GrDawnOpsRenderPass*>(renderPass)->submit();
}
GrFence SK_WARN_UNUSED_RESULT GrDawnGpu::insertFence() {
wgpu::FenceDescriptor desc;
wgpu::Fence fence = fQueue.CreateFence(&desc);
return reinterpret_cast<GrFence>(new Fence(fDevice, fence));
}
bool GrDawnGpu::waitFence(GrFence fence) {
return reinterpret_cast<Fence*>(fence)->check();
}
void GrDawnGpu::deleteFence(GrFence fence) const {
delete reinterpret_cast<Fence*>(fence);
}
std::unique_ptr<GrSemaphore> SK_WARN_UNUSED_RESULT GrDawnGpu::makeSemaphore(bool isOwned) {
SkASSERT(!"unimplemented");
return nullptr;
}
std::unique_ptr<GrSemaphore> GrDawnGpu::wrapBackendSemaphore(
const GrBackendSemaphore& semaphore,
GrResourceProvider::SemaphoreWrapType wrapType,
GrWrapOwnership ownership) {
SkASSERT(!"unimplemented");
return nullptr;
}
void GrDawnGpu::insertSemaphore(GrSemaphore* semaphore) {
SkASSERT(!"unimplemented");
}
void GrDawnGpu::waitSemaphore(GrSemaphore* semaphore) {
SkASSERT(!"unimplemented");
}
void GrDawnGpu::checkFinishProcs() {
fFinishCallbacks.check();
}
void GrDawnGpu::finishOutstandingGpuWork() {
this->waitOnAllBusyStagingBuffers();
}
std::unique_ptr<GrSemaphore> GrDawnGpu::prepareTextureForCrossContextUsage(GrTexture* texture) {
SkASSERT(!"unimplemented");
return nullptr;
}
sk_sp<GrDawnProgram> GrDawnGpu::getOrCreateRenderPipeline(
GrRenderTarget* rt,
const GrProgramInfo& programInfo) {
GrProgramDesc desc = this->caps()->makeDesc(rt, programInfo);
if (!desc.isValid()) {
return nullptr;
}
if (sk_sp<GrDawnProgram>* program = fRenderPipelineCache.find(desc)) {
return *program;
}
wgpu::TextureFormat colorFormat;
SkAssertResult(programInfo.backendFormat().asDawnFormat(&colorFormat));
wgpu::TextureFormat stencilFormat = wgpu::TextureFormat::Depth24PlusStencil8;
bool hasDepthStencil = rt->getStencilAttachment() != nullptr;
sk_sp<GrDawnProgram> program = GrDawnProgramBuilder::Build(
this, rt, programInfo, colorFormat,
hasDepthStencil, stencilFormat, &desc);
fRenderPipelineCache.insert(desc, program);
return program;
}
wgpu::Sampler GrDawnGpu::getOrCreateSampler(GrSamplerState samplerState) {
auto i = fSamplers.find(samplerState);
if (i != fSamplers.end()) {
return i->second;
}
wgpu::SamplerDescriptor desc;
desc.addressModeU = to_dawn_address_mode(samplerState.wrapModeX());
desc.addressModeV = to_dawn_address_mode(samplerState.wrapModeY());
desc.addressModeW = wgpu::AddressMode::ClampToEdge;
desc.magFilter = desc.minFilter = to_dawn_filter_mode(samplerState.filter());
desc.mipmapFilter = to_dawn_mipmap_mode(samplerState.mipmapMode());
wgpu::Sampler sampler = device().CreateSampler(&desc);
fSamplers.insert(std::pair<GrSamplerState, wgpu::Sampler>(samplerState, sampler));
return sampler;
}
GrDawnRingBuffer::Slice GrDawnGpu::allocateUniformRingBufferSlice(int size) {
return fUniformRingBuffer.allocate(size);
}
void GrDawnGpu::appendCommandBuffer(wgpu::CommandBuffer commandBuffer) {
if (commandBuffer) {
fCommandBuffers.push_back(commandBuffer);
}
}
wgpu::CommandEncoder GrDawnGpu::getCopyEncoder() {
if (!fCopyEncoder) {
fCopyEncoder = fDevice.CreateCommandEncoder();
}
return fCopyEncoder;
}
void GrDawnGpu::flushCopyEncoder() {
if (fCopyEncoder) {
fCommandBuffers.push_back(fCopyEncoder.Finish());
fCopyEncoder = nullptr;
}
}
void GrDawnGpu::moveStagingBuffersToBusyAndMapAsync() {
for (size_t i = 0; i < fSubmittedStagingBuffers.size(); ++i) {
GrDawnBuffer* buffer = static_cast<GrDawnBuffer*>(fSubmittedStagingBuffers[i].get());
buffer->mapWriteAsync();
fBusyStagingBuffers.push_back(std::move(fSubmittedStagingBuffers[i]));
}
fSubmittedStagingBuffers.clear();
}
SkSL::String GrDawnGpu::SkSLToSPIRV(const char* shaderString, SkSL::ProgramKind kind, bool flipY,
uint32_t rtHeightOffset, SkSL::Program::Inputs* inputs) {
auto errorHandler = this->getContext()->priv().getShaderErrorHandler();
SkSL::Program::Settings settings;
settings.fFlipY = flipY;
settings.fRTHeightOffset = rtHeightOffset;
settings.fRTHeightBinding = 0;
settings.fRTHeightSet = 0;
std::unique_ptr<SkSL::Program> program = this->shaderCompiler()->convertProgram(
kind,
shaderString,
settings);
if (!program) {
errorHandler->compileError(shaderString, this->shaderCompiler()->errorText().c_str());
return "";
}
if (inputs) {
*inputs = program->fInputs;
}
SkSL::String code;
if (!this->shaderCompiler()->toSPIRV(*program, &code)) {
errorHandler->compileError(shaderString, this->shaderCompiler()->errorText().c_str());
return "";
}
return code;
}
wgpu::ShaderModule GrDawnGpu::createShaderModule(const SkSL::String& spirvSource) {
wgpu::ShaderModuleSPIRVDescriptor desc;
desc.codeSize = spirvSource.size() / 4;
desc.code = reinterpret_cast<const uint32_t*>(spirvSource.c_str());
wgpu::ShaderModuleDescriptor smDesc;
smDesc.nextInChain = &desc;
return fDevice.CreateShaderModule(&smDesc);
}