| /* | |
| * Copyright 2022 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/graphite/dawn/DawnTexture.h" | |
| #include "include/core/SkTraceMemoryDump.h" | |
| #include "include/gpu/MutableTextureState.h" | |
| #include "include/private/SkLog.h" | |
| #include "src/core/SkMipmap.h" | |
| #include "src/gpu/graphite/Sampler.h" | |
| #include "src/gpu/graphite/TextureUtils.h" | |
| #include "src/gpu/graphite/dawn/DawnCaps.h" | |
| #include "src/gpu/graphite/dawn/DawnGraphiteUtils.h" | |
| #include "src/gpu/graphite/dawn/DawnSharedContext.h" | |
| namespace skgpu::graphite { | |
| wgpu::Texture DawnTexture::MakeDawnTexture(const DawnSharedContext* sharedContext, | |
| SkISize dimensions, | |
| const TextureInfo& info) { | |
| const auto* caps = sharedContext->dawnCaps(); | |
| if (dimensions.width() > caps->maxTextureSize() || | |
| dimensions.height() > caps->maxTextureSize()) { | |
| SKIA_LOG_E("Texture creation failure: dimensions %d x %d too large.", | |
| dimensions.width(), dimensions.height()); | |
| return {}; | |
| } | |
| const auto& dawnInfo = TextureInfoPriv::Get<DawnTextureInfo>(info); | |
| if (dawnInfo.fUsage & wgpu::TextureUsage::TextureBinding && | |
| !caps->isTexturable(info, /*allowMSAA=*/true) && | |
| !caps->isReadable(info, /*allowMSAA=*/true)) { | |
| return {}; | |
| } | |
| if (dawnInfo.fUsage & wgpu::TextureUsage::RenderAttachment && !caps->isRenderable(info)) { | |
| return {}; | |
| } | |
| if (dawnInfo.fUsage & wgpu::TextureUsage::StorageBinding && !caps->isStorage(info)) { | |
| return {}; | |
| } | |
| #if !defined(__EMSCRIPTEN__) | |
| // If a non-default YCbCr descriptor is provided, either the vkFormat or the externalFormat must | |
| // be defined. | |
| if (DawnDescriptorIsValid(dawnInfo.fYcbcrVkDescriptor) && | |
| dawnInfo.fYcbcrVkDescriptor.vkFormat == 0 && | |
| dawnInfo.fYcbcrVkDescriptor.externalFormat == 0) { | |
| return {}; | |
| } | |
| #endif | |
| int numMipLevels = 1; | |
| if (info.mipmapped() == Mipmapped::kYes) { | |
| numMipLevels = SkMipmap::ComputeLevelCount(dimensions) + 1; | |
| } | |
| wgpu::TextureDescriptor desc; | |
| desc.usage = dawnInfo.fUsage; | |
| desc.dimension = wgpu::TextureDimension::e2D; | |
| desc.size.width = dimensions.width(); | |
| desc.size.height = dimensions.height(); | |
| desc.size.depthOrArrayLayers = 1; | |
| desc.format = dawnInfo.fFormat; | |
| desc.mipLevelCount = numMipLevels; | |
| desc.sampleCount = (uint8_t) info.sampleCount(); | |
| desc.viewFormatCount = 0; | |
| desc.viewFormats = nullptr; | |
| auto texture = sharedContext->device().CreateTexture(&desc); | |
| if (!texture) { | |
| return {}; | |
| } | |
| return texture; | |
| } | |
| static bool has_transient_usage(const TextureInfo& info) { | |
| #if !defined(__EMSCRIPTEN__) | |
| const auto& dawnInfo = TextureInfoPriv::Get<DawnTextureInfo>(info); | |
| return dawnInfo.fUsage & wgpu::TextureUsage::TransientAttachment; | |
| #else | |
| return false; | |
| #endif | |
| } | |
| DawnTexture::DawnTexture(const DawnSharedContext* sharedContext, | |
| SkISize dimensions, | |
| const TextureInfo& info, | |
| wgpu::Texture texture, | |
| wgpu::TextureView sampleTextureView, | |
| wgpu::TextureView renderTextureView, | |
| Ownership ownership, | |
| std::string_view label) | |
| : Texture(sharedContext, | |
| dimensions, | |
| info, | |
| /*isTransient=*/has_transient_usage(info), | |
| /*mutableState=*/nullptr, | |
| ownership, | |
| label) | |
| , fTexture(std::move(texture)) | |
| , fSampleTextureView(std::move(sampleTextureView)) | |
| , fRenderTextureView(std::move(renderTextureView)) { | |
| // Update the newly-created underlying GPU object's label to match the Resource's | |
| this->synchronizeBackendLabel(); | |
| } | |
| // static | |
| std::pair<wgpu::TextureView, wgpu::TextureView> DawnTexture::CreateTextureViews( | |
| const wgpu::Texture& texture, const TextureInfo& info) { | |
| const auto& dawnInfo = TextureInfoPriv::Get<DawnTextureInfo>(info); | |
| const auto aspect = dawnInfo.fAspect; | |
| if (aspect == wgpu::TextureAspect::All) { | |
| wgpu::TextureViewDescriptor viewDesc = {}; | |
| viewDesc.dimension = wgpu::TextureViewDimension::e2D; | |
| viewDesc.baseArrayLayer = dawnInfo.fSlice; | |
| viewDesc.arrayLayerCount = 1; | |
| #if !defined(__EMSCRIPTEN__) | |
| // Ensure that the TextureView is configured to use YCbCr sampling if the Texture is | |
| // doing so. | |
| const wgpu::YCbCrVkDescriptor& ycbcrDesc = dawnInfo.fYcbcrVkDescriptor; | |
| if (DawnDescriptorIsValid(ycbcrDesc)) { | |
| viewDesc.nextInChain = &ycbcrDesc; | |
| } | |
| #endif | |
| wgpu::TextureView sampleTextureView = texture.CreateView(&viewDesc); | |
| wgpu::TextureView renderTextureView; | |
| if (info.mipmapped() == Mipmapped::kYes) { | |
| viewDesc.baseMipLevel = 0; | |
| viewDesc.mipLevelCount = 1; | |
| renderTextureView = texture.CreateView(&viewDesc); | |
| } else { | |
| renderTextureView = sampleTextureView; | |
| } | |
| return {sampleTextureView, renderTextureView}; | |
| } | |
| #if defined(__EMSCRIPTEN__) | |
| SkASSERT(false); | |
| return {}; | |
| #else | |
| SkASSERT(aspect == wgpu::TextureAspect::Plane0Only || | |
| aspect == wgpu::TextureAspect::Plane1Only || | |
| aspect == wgpu::TextureAspect::Plane2Only); | |
| wgpu::TextureView planeTextureView; | |
| wgpu::TextureViewDescriptor planeViewDesc = {}; | |
| planeViewDesc.format = dawnInfo.fViewFormat; | |
| planeViewDesc.dimension = wgpu::TextureViewDimension::e2D; | |
| planeViewDesc.aspect = aspect; | |
| planeViewDesc.baseArrayLayer = dawnInfo.fSlice; | |
| planeViewDesc.arrayLayerCount = 1; | |
| planeTextureView = texture.CreateView(&planeViewDesc); | |
| return {planeTextureView, planeTextureView}; | |
| #endif | |
| } | |
| sk_sp<Texture> DawnTexture::Make(const DawnSharedContext* sharedContext, | |
| SkISize dimensions, | |
| const TextureInfo& info, | |
| std::string_view label) { | |
| auto texture = MakeDawnTexture(sharedContext, dimensions, info); | |
| if (!texture) { | |
| return {}; | |
| } | |
| auto [sampleTextureView, renderTextureView] = CreateTextureViews(texture, info); | |
| return sk_sp<Texture>(new DawnTexture(sharedContext, | |
| dimensions, | |
| info, | |
| std::move(texture), | |
| std::move(sampleTextureView), | |
| std::move(renderTextureView), | |
| Ownership::kOwned, | |
| label)); | |
| } | |
| sk_sp<Texture> DawnTexture::MakeWrapped(const DawnSharedContext* sharedContext, | |
| SkISize dimensions, | |
| const TextureInfo& info, | |
| wgpu::Texture texture, | |
| std::string_view label) { | |
| if (!texture) { | |
| SKIA_LOG_E("No valid texture passed into MakeWrapped\n"); | |
| return {}; | |
| } | |
| auto [sampleTextureView, renderTextureView] = CreateTextureViews(texture, info); | |
| return sk_sp<Texture>(new DawnTexture(sharedContext, | |
| dimensions, | |
| info, | |
| std::move(texture), | |
| std::move(sampleTextureView), | |
| std::move(renderTextureView), | |
| Ownership::kWrapped, | |
| label)); | |
| } | |
| sk_sp<Texture> DawnTexture::MakeWrapped(const DawnSharedContext* sharedContext, | |
| SkISize dimensions, | |
| const TextureInfo& info, | |
| const wgpu::TextureView& textureView, | |
| std::string_view label) { | |
| if (!textureView) { | |
| SKIA_LOG_E("No valid texture view passed into MakeWrapped\n"); | |
| return {}; | |
| } | |
| return sk_sp<Texture>(new DawnTexture(sharedContext, | |
| dimensions, | |
| info, | |
| /*texture=*/nullptr, | |
| /*sampleTextureView=*/textureView, | |
| /*renderTextureView=*/textureView, | |
| Ownership::kWrapped, | |
| label)); | |
| } | |
| void DawnTexture::freeGpuData() { | |
| if (this->ownership() != Ownership::kWrapped && fTexture) { | |
| // Destroy the texture even if it is still referenced by other BindGroup or views. | |
| // Graphite should already guarantee that all command buffers using this texture (indirectly | |
| // via BindGroup or views) are already completed. | |
| fTexture.Destroy(); | |
| } | |
| fTexture = nullptr; | |
| fSampleTextureView = nullptr; | |
| fRenderTextureView = nullptr; | |
| } | |
| void DawnTexture::setBackendLabel(char const* label) { | |
| if (!sharedContext()->caps()->setBackendLabels()) { | |
| return; | |
| } | |
| SkASSERT(label); | |
| // Wrapped texture views won't have an associated texture here. | |
| if (fTexture) { | |
| fTexture.SetLabel(label); | |
| } | |
| // But we always have the texture views available. | |
| SkASSERT(fSampleTextureView); | |
| SkASSERT(fRenderTextureView); | |
| if (fSampleTextureView.Get() == fRenderTextureView.Get()) { | |
| fSampleTextureView.SetLabel(SkStringPrintf("%s_%s", label, "_TextureView").c_str()); | |
| } else { | |
| fSampleTextureView.SetLabel(SkStringPrintf("%s_%s", label, "_SampleTextureView").c_str()); | |
| fRenderTextureView.SetLabel(SkStringPrintf("%s_%s", label, "_RenderTextureView").c_str()); | |
| } | |
| } | |
| const wgpu::BindGroup* DawnTexture::getCachedSingleTextureBindGroup(const Sampler* sampler) const { | |
| SkASSERT(sampler); | |
| for (auto& cachedGroup : fCachedSingleTextureBindGroups) { | |
| if (cachedGroup.first->uniqueID() == sampler->uniqueID()) { | |
| return &cachedGroup.second; | |
| } | |
| } | |
| return nullptr; | |
| } | |
| void DawnTexture::addCachedSingleTextureBindGroup(wgpu::BindGroup bindGroup, | |
| const Sampler* sampler) const { | |
| SkASSERT(sampler); | |
| fCachedSingleTextureBindGroups.push_back({sampler, bindGroup}); | |
| } | |
| } // namespace skgpu::graphite |