blob: 783fa9296773d40599dfc32aad1c781a14d04f72 [file] [log] [blame]
// Copyright 2017 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "dawn/native/opengl/TextureGL.h"
#include <algorithm>
#include <limits>
#include <utility>
#include "dawn/common/Assert.h"
#include "dawn/common/Constants.h"
#include "dawn/common/Math.h"
#include "dawn/native/ChainUtils.h"
#include "dawn/native/Device.h"
#include "dawn/native/EnumMaskIterator.h"
#include "dawn/native/Queue.h"
#include "dawn/native/opengl/BufferGL.h"
#include "dawn/native/opengl/CommandBufferGL.h"
#include "dawn/native/opengl/DeviceGL.h"
#include "dawn/native/opengl/SharedFenceGL.h"
#include "dawn/native/opengl/SharedTextureMemoryGL.h"
#include "dawn/native/opengl/UtilsGL.h"
namespace dawn::native::opengl {
namespace {
GLenum TargetForTextureViewDimension(wgpu::TextureViewDimension dimension, uint32_t sampleCount) {
switch (dimension) {
case wgpu::TextureViewDimension::e1D:
case wgpu::TextureViewDimension::e2D:
return (sampleCount > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;
case wgpu::TextureViewDimension::e2DArray:
if (sampleCount > 1) {
return GL_TEXTURE_2D_MULTISAMPLE;
}
DAWN_ASSERT(sampleCount == 1);
return GL_TEXTURE_2D_ARRAY;
case wgpu::TextureViewDimension::Cube:
DAWN_ASSERT(sampleCount == 1);
return GL_TEXTURE_CUBE_MAP;
case wgpu::TextureViewDimension::CubeArray:
DAWN_ASSERT(sampleCount == 1);
return GL_TEXTURE_CUBE_MAP_ARRAY;
case wgpu::TextureViewDimension::e3D:
DAWN_ASSERT(sampleCount == 1);
return GL_TEXTURE_3D;
case wgpu::TextureViewDimension::Undefined:
default:
DAWN_UNREACHABLE();
}
}
// Note this only applies to Desktop OpenGL (texture views don't exist in GLES).
// In compatibility mode, validation should mean we never need texture views.
bool RequiresCreatingNewTextureView(
const TextureBase* texture,
const UnpackedPtr<TextureViewDescriptor>& textureViewDescriptor) {
if (ToBackend(texture->GetDevice())->IsCompatibilityMode()) {
return false;
}
constexpr wgpu::TextureUsage kShaderUsageNeedsView =
wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::TextureBinding;
constexpr wgpu::TextureUsage kUsageNeedsView =
kShaderUsageNeedsView | wgpu::TextureUsage::RenderAttachment;
if (!(texture->GetInternalUsage() & kUsageNeedsView)) {
return false;
}
if (texture->GetFormat().format != textureViewDescriptor->format &&
!texture->GetFormat().HasDepthOrStencil()) {
// Color format reinterpretation required. Note: Depth/stencil formats don't support
// reinterpretation.
return true;
}
// Reinterpretation not required. Now, we only need a new view if the view dimension or
// set of subresources for the shader is different from the base texture.
if (!(texture->GetInternalUsage() & kShaderUsageNeedsView)) {
return false;
}
if (texture->GetArrayLayers() != textureViewDescriptor->arrayLayerCount ||
(texture->GetArrayLayers() == 1 && texture->GetDimension() == wgpu::TextureDimension::e2D &&
textureViewDescriptor->dimension == wgpu::TextureViewDimension::e2DArray)) {
// If the view has a different number of array layers, we need a new view.
// And, if the original texture is a 2D texture with one array layer, we need a new
// view to view it as a 2D array texture.
return true;
}
if (ToBackend(texture)->GetGLFormat().format == GL_DEPTH_STENCIL &&
(texture->GetUsage() & wgpu::TextureUsage::TextureBinding) &&
textureViewDescriptor->aspect == wgpu::TextureAspect::StencilOnly) {
// We need a separate view for one of the depth or stencil planes
// because each glTextureView needs it's own handle to set
// GL_DEPTH_STENCIL_TEXTURE_MODE. Choose the stencil aspect for the
// extra handle since it is likely sampled less often.
return true;
}
// Note: For formats with <4 channels, this could be refined to consider that some channels are
// nonexistent. We don't bother to optimize that, because such swizzles are not actually useful.
// (Also, this code is only reached on Desktop GL anyway.)
if (auto* swizzleDesc = textureViewDescriptor.Get<TextureComponentSwizzleDescriptor>()) {
auto swizzle = swizzleDesc->swizzle.WithTrivialFrontendDefaults();
if (*ToCppAPI(&swizzle) != kRGBASwizzle) {
return true;
}
}
return false;
}
MaybeError AllocateTexture(const OpenGLFunctions& gl,
GLenum target,
GLsizei samples,
GLuint levels,
const GLFormat& format,
const Extent3D& size) {
if (format.isSupportedForTextureStorage || target == GL_TEXTURE_2D_MULTISAMPLE) {
// glTextureView() requires the value of GL_TEXTURE_IMMUTABLE_FORMAT for origtexture to
// be GL_TRUE, so the storage of the texture must be allocated with glTexStorage*D.
// https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTextureView.xhtml
// There is no fallback for multisampled textures. They must be allocated with
// glTexStorage2DMultisample
switch (target) {
case GL_TEXTURE_2D_ARRAY:
case GL_TEXTURE_CUBE_MAP_ARRAY:
case GL_TEXTURE_3D:
DAWN_GL_TRY_ALWAYS_CHECK(
gl, TexStorage3D(target, levels, format.internalFormat, size.width, size.height,
size.depthOrArrayLayers));
break;
case GL_TEXTURE_2D:
case GL_TEXTURE_CUBE_MAP:
DAWN_GL_TRY_ALWAYS_CHECK(gl, TexStorage2D(target, levels, format.internalFormat,
size.width, size.height));
break;
case GL_TEXTURE_2D_MULTISAMPLE:
DAWN_GL_TRY_ALWAYS_CHECK(
gl, TexStorage2DMultisample(target, samples, format.internalFormat, size.width,
size.height, true));
break;
default:
DAWN_UNREACHABLE();
}
} else {
// Allocate the texture using multiple glTexImage. This should only happen in compat and the
// resulting texture will not be usable with glTextureView
// When using glTexImage, make sure there is no unpack buffer bound or data would be copied
// from whatever buffer happens to be bound.
DAWN_GL_TRY_ALWAYS_CHECK(gl, BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
// Array texture formats have the same depth for all mip levels.
bool constantSizeForAllLevels =
target == GL_TEXTURE_2D_ARRAY || target == GL_TEXTURE_CUBE_MAP_ARRAY;
for (GLuint level = 0; level < levels; level++) {
Extent3D levelSize{
std::max(size.width >> level, 1u), std::max(size.height >> level, 1u),
constantSizeForAllLevels ? size.depthOrArrayLayers
: std::max(size.depthOrArrayLayers >> level, 1u)};
switch (target) {
case GL_TEXTURE_2D_ARRAY:
case GL_TEXTURE_CUBE_MAP_ARRAY:
case GL_TEXTURE_3D:
DAWN_GL_TRY_ALWAYS_CHECK(
gl, TexImage3D(target, level, format.format, levelSize.width,
levelSize.height, levelSize.depthOrArrayLayers, 0,
format.format, format.type, nullptr));
break;
case GL_TEXTURE_2D:
DAWN_GL_TRY_ALWAYS_CHECK(
gl, TexImage2D(target, level, format.format, levelSize.width,
levelSize.height, 0, format.format, format.type, nullptr));
break;
case GL_TEXTURE_CUBE_MAP:
for (size_t faceIdx = 0; faceIdx < 6; faceIdx++) {
GLenum faceTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + faceIdx;
DAWN_GL_TRY_ALWAYS_CHECK(
gl,
TexImage2D(faceTarget, level, format.format, levelSize.width,
levelSize.height, 0, format.format, format.type, nullptr));
}
break;
default:
DAWN_UNREACHABLE();
}
}
}
return {};
}
MaybeError FramebufferTextureHelper(const OpenGLFunctions& gl,
GLenum textarget,
GLenum target,
GLenum attachment,
GLuint textureHandle,
GLuint mipLevel,
GLuint arrayLayer) {
switch (textarget) {
case GL_TEXTURE_2D_ARRAY:
case GL_TEXTURE_CUBE_MAP_ARRAY:
case GL_TEXTURE_3D:
DAWN_GL_TRY(gl, FramebufferTextureLayer(target, attachment, textureHandle, mipLevel,
arrayLayer));
break;
case GL_TEXTURE_CUBE_MAP: {
GLenum cubeTexTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + arrayLayer;
DAWN_GL_TRY(gl, FramebufferTexture2D(target, attachment, cubeTexTarget, textureHandle,
mipLevel));
break;
}
default:
DAWN_ASSERT(textarget == GL_TEXTURE_2D || textarget == GL_TEXTURE_2D_MULTISAMPLE);
DAWN_GL_TRY(
gl, FramebufferTexture2D(target, attachment, textarget, textureHandle, mipLevel));
break;
}
return {};
}
} // namespace
// Texture
// static
ResultOrError<Ref<Texture>> Texture::Create(Device* device,
const UnpackedPtr<TextureDescriptor>& descriptor) {
// Wrap the handle in a Texture class early so that it is deleted if initialization fails
Ref<Texture> texture = AcquireRef(new Texture(device, descriptor, 0, OwnsHandle::Yes));
bool clear = device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting);
DAWN_TRY(device->EnqueueGL([texture, clear](const OpenGLFunctions& gl) -> MaybeError {
DAWN_GL_TRY(gl, GenTextures(1, &texture->mHandle));
GLenum target = texture->GetGLTarget();
uint32_t levels = texture->GetNumMipLevels();
const GLFormat& glFormat = texture->GetGLFormat();
DAWN_GL_TRY(gl, BindTexture(target, texture->mHandle));
DAWN_TRY(AllocateTexture(gl, target, texture->GetSampleCount(), levels, glFormat,
texture->GetBaseSize()));
// The texture is not complete if it uses mipmapping and not all levels up to
// MAX_LEVEL have been defined.
DAWN_GL_TRY(gl, TexParameteri(target, GL_TEXTURE_MAX_LEVEL, levels - 1));
if (clear) {
DAWN_TRY(texture->ClearTexture(gl, texture->GetAllSubresources(),
TextureBase::ClearValue::NonZero));
}
return {};
}));
return std::move(texture);
}
// static
ResultOrError<Ref<Texture>> Texture::CreateFromSharedTextureMemory(
SharedTextureMemory* memory,
const UnpackedPtr<TextureDescriptor>& descriptor) {
Device* device = ToBackend(memory->GetDevice());
Ref<Texture> texture = AcquireRef(new Texture(device, descriptor, 0, OwnsHandle::Yes));
DAWN_TRY(device->EnqueueGL([texture, memory = Ref<SharedTextureMemory>(memory)](
const OpenGLFunctions& gl) -> MaybeError {
DAWN_TRY_ASSIGN(texture->mHandle, memory->GenerateGLTexture(gl));
return {};
}));
texture->mSharedResourceMemoryContents = memory->GetContents();
return texture;
}
Texture::Texture(Device* device,
const UnpackedPtr<TextureDescriptor>& descriptor,
GLuint handle,
OwnsHandle ownsHandle)
: TextureBase(device, descriptor), mHandle(handle), mOwnsHandle(ownsHandle) {
mTarget = TargetForTextureViewDimension(GetCompatibilityTextureBindingViewDimension(),
descriptor->sampleCount);
}
Texture::~Texture() {}
void Texture::DestroyImpl(DestroyReason reason) {
TextureBase::DestroyImpl(reason);
if (mOwnsHandle == OwnsHandle::Yes) {
IgnoreErrors(
ToBackend(GetDevice())
->EnqueueDestroyGL(this, &Texture::GetHandle, reason,
[](const OpenGLFunctions& gl, GLuint handle) -> MaybeError {
DAWN_GL_TRY_IGNORE_ERRORS(gl, DeleteTextures(1, &handle));
return {};
}));
}
}
GLuint Texture::GetHandle() const {
return mHandle;
}
GLenum Texture::GetGLTarget() const {
return mTarget;
}
const GLFormat& Texture::GetGLFormat() const {
return ToBackend(GetDevice())->GetGLFormat(GetFormat());
}
MaybeError Texture::ClearTexture(const OpenGLFunctions& gl,
const SubresourceRange& range,
TextureBase::ClearValue clearValue) {
Device* device = ToBackend(GetDevice());
uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1;
float fClearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0.f : 1.f;
if (GetFormat().IsRenderable()) {
if (range.aspects & (Aspect::Depth | Aspect::Stencil)) {
GLfloat depth = fClearColor;
GLint stencil = clearColor;
if (range.aspects & Aspect::Depth) {
DAWN_GL_TRY(gl, DepthMask(GL_TRUE));
}
if (range.aspects & Aspect::Stencil) {
DAWN_GL_TRY(gl, StencilMask(GetStencilMaskFromStencilFormat(GetFormat().format)));
}
auto DoClear = [&](Aspect aspects) -> MaybeError {
if (aspects == (Aspect::Depth | Aspect::Stencil)) {
DAWN_GL_TRY(gl, ClearBufferfi(GL_DEPTH_STENCIL, 0, depth, stencil));
} else if (aspects == Aspect::Depth) {
DAWN_GL_TRY(gl, ClearBufferfv(GL_DEPTH, 0, &depth));
} else if (aspects == Aspect::Stencil) {
DAWN_GL_TRY(gl, ClearBufferiv(GL_STENCIL, 0, &stencil));
} else {
DAWN_UNREACHABLE();
}
return {};
};
GLuint framebuffer = 0;
DAWN_GL_TRY(gl, GenFramebuffers(1, &framebuffer));
DAWN_GL_TRY(gl, BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer));
DAWN_GL_TRY(gl, Disable(GL_SCISSOR_TEST));
GLenum attachment;
if (range.aspects == (Aspect::Depth | Aspect::Stencil)) {
attachment = GL_DEPTH_STENCIL_ATTACHMENT;
} else if (range.aspects == Aspect::Depth) {
attachment = GL_DEPTH_ATTACHMENT;
} else if (range.aspects == Aspect::Stencil) {
attachment = GL_STENCIL_ATTACHMENT;
} else {
DAWN_UNREACHABLE();
}
for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
++level) {
for (uint32_t layer = range.baseArrayLayer;
layer < range.baseArrayLayer + range.layerCount; ++layer) {
Aspect aspectsToClear = Aspect::None;
for (Aspect aspect : IterateEnumMask(range.aspects)) {
if (clearValue == TextureBase::ClearValue::Zero &&
IsSubresourceContentInitialized(
SubresourceRange::SingleMipAndLayer(level, layer, aspect))) {
// Skip lazy clears if already initialized.
continue;
}
aspectsToClear |= aspect;
}
if (aspectsToClear == Aspect::None) {
continue;
}
DAWN_TRY(FramebufferTextureHelper(gl, mTarget, GL_DRAW_FRAMEBUFFER, attachment,
GetHandle(), level, layer));
DAWN_TRY(DoClear(aspectsToClear));
}
}
DAWN_GL_TRY(gl, Enable(GL_SCISSOR_TEST));
DAWN_GL_TRY(gl, DeleteFramebuffers(1, &framebuffer));
} else {
DAWN_ASSERT(range.aspects == Aspect::Color);
// For gl.ClearBufferiv/uiv calls
constexpr std::array<GLuint, 4> kClearColorDataUint0 = {0u, 0u, 0u, 0u};
constexpr std::array<GLuint, 4> kClearColorDataUint1 = {1u, 1u, 1u, 1u};
std::array<GLuint, 4> clearColorData;
clearColorData.fill((clearValue == TextureBase::ClearValue::Zero) ? 0u : 1u);
// For gl.ClearBufferfv calls
constexpr std::array<GLfloat, 4> kClearColorDataFloat0 = {0.f, 0.f, 0.f, 0.f};
constexpr std::array<GLfloat, 4> kClearColorDataFloat1 = {1.f, 1.f, 1.f, 1.f};
std::array<GLfloat, 4> fClearColorData;
fClearColorData.fill((clearValue == TextureBase::ClearValue::Zero) ? 0.f : 1.f);
static constexpr uint32_t MAX_TEXEL_SIZE = 16;
const TexelBlockInfo& blockInfo = GetFormat().GetAspectInfo(Aspect::Color).block;
DAWN_ASSERT(blockInfo.byteSize <= MAX_TEXEL_SIZE);
// For gl.ClearTexSubImage calls
constexpr std::array<GLbyte, MAX_TEXEL_SIZE> kClearColorDataBytes0 = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
constexpr std::array<GLbyte, MAX_TEXEL_SIZE> kClearColorDataBytes255 = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
TextureComponentType baseType = GetFormat().GetAspectInfo(Aspect::Color).baseType;
const GLFormat& glFormat = GetGLFormat();
const auto dimension = GetDimension();
for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
++level) {
Extent3D mipSize = GetMipLevelSingleSubresourcePhysicalSize(level, Aspect::Color);
for (uint32_t layer = range.baseArrayLayer;
layer < range.baseArrayLayer + range.layerCount; ++layer) {
if (clearValue == TextureBase::ClearValue::Zero &&
IsSubresourceContentInitialized(
SubresourceRange::SingleMipAndLayer(level, layer, Aspect::Color))) {
// Skip lazy clears if already initialized.
continue;
}
if (gl.IsAtLeastGL(4, 4)) {
DAWN_GL_TRY(gl, ClearTexSubImage(mHandle, static_cast<GLint>(level), 0, 0,
static_cast<GLint>(layer), mipSize.width,
mipSize.height, mipSize.depthOrArrayLayers,
glFormat.format, glFormat.type,
clearValue == TextureBase::ClearValue::Zero
? kClearColorDataBytes0.data()
: kClearColorDataBytes255.data()));
continue;
}
GLuint framebuffer = 0;
DAWN_GL_TRY(gl, GenFramebuffers(1, &framebuffer));
DAWN_GL_TRY(gl, BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer));
GLenum attachment = GL_COLOR_ATTACHMENT0;
DAWN_GL_TRY(gl, DrawBuffers(1, &attachment));
DAWN_GL_TRY(gl, Disable(GL_SCISSOR_TEST));
DAWN_GL_TRY(gl, ColorMask(true, true, true, true));
auto DoClear = [&]() -> MaybeError {
switch (baseType) {
case TextureComponentType::Float: {
DAWN_GL_TRY(
gl, ClearBufferfv(GL_COLOR, 0,
clearValue == TextureBase::ClearValue::Zero
? kClearColorDataFloat0.data()
: kClearColorDataFloat1.data()));
break;
}
case TextureComponentType::Uint: {
DAWN_GL_TRY(
gl, ClearBufferuiv(GL_COLOR, 0,
clearValue == TextureBase::ClearValue::Zero
? kClearColorDataUint0.data()
: kClearColorDataUint1.data()));
break;
}
case TextureComponentType::Sint: {
DAWN_GL_TRY(gl, ClearBufferiv(
GL_COLOR, 0,
reinterpret_cast<const GLint*>(
clearValue == TextureBase::ClearValue::Zero
? kClearColorDataUint0.data()
: kClearColorDataUint1.data())));
break;
}
}
return {};
};
if (dimension == wgpu::TextureDimension::e3D) {
uint32_t depth =
GetMipLevelSingleSubresourceVirtualSize(level, Aspect::Color)
.depthOrArrayLayers;
for (GLint z = 0; z < static_cast<GLint>(depth); ++z) {
DAWN_GL_TRY(gl, FramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attachment,
GetHandle(), level, z));
DAWN_TRY(DoClear());
}
} else {
DAWN_TRY(FramebufferTextureHelper(gl, mTarget, GL_DRAW_FRAMEBUFFER,
attachment, GetHandle(), level, layer));
DAWN_TRY(DoClear());
}
DAWN_GL_TRY(gl, Enable(GL_SCISSOR_TEST));
DAWN_GL_TRY(gl, DeleteFramebuffers(1, &framebuffer));
DAWN_GL_TRY(gl, BindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
}
}
}
} else {
DAWN_ASSERT(range.aspects == Aspect::Color);
// create temp buffer with clear color to copy to the texture image
const TexelBlockInfo& blockInfo = GetFormat().GetAspectInfo(Aspect::Color).block;
DAWN_ASSERT(kTextureBytesPerRowAlignment % blockInfo.byteSize == 0);
Extent3D largestMipSize =
GetMipLevelSingleSubresourcePhysicalSize(range.baseMipLevel, Aspect::Color);
uint32_t bytesPerRow =
Align((largestMipSize.width / blockInfo.width) * blockInfo.byteSize, 4);
// Make sure that we are not rounding
DAWN_ASSERT(bytesPerRow % blockInfo.byteSize == 0);
DAWN_ASSERT(largestMipSize.height % blockInfo.height == 0);
uint64_t bufferSize64 = static_cast<uint64_t>(bytesPerRow) *
(largestMipSize.height / blockInfo.height) *
largestMipSize.depthOrArrayLayers;
if (bufferSize64 > std::numeric_limits<size_t>::max()) {
return DAWN_OUT_OF_MEMORY_ERROR("Unable to allocate buffer.");
}
size_t bufferSize = static_cast<size_t>(bufferSize64);
dawn::native::BufferDescriptor descriptor = {};
descriptor.mappedAtCreation = true;
descriptor.usage = wgpu::BufferUsage::CopySrc;
descriptor.size = bufferSize;
// We don't count the lazy clear of srcBuffer because it is an internal buffer.
// TODO(natlee@microsoft.com): use Dynamic Uploader here for temp buffer
Ref<Buffer> srcBuffer;
DAWN_TRY_ASSIGN(srcBuffer, Buffer::CreateInternalBuffer(device, &descriptor, false));
// Fill the buffer with clear color
memset(srcBuffer->GetMappedRange(0, bufferSize), clearColor, bufferSize);
DAWN_TRY(srcBuffer->Unmap());
DAWN_GL_TRY(gl, BindBuffer(GL_PIXEL_UNPACK_BUFFER, srcBuffer->GetHandle()));
for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
++level) {
TextureCopy textureCopy;
textureCopy.texture = this;
textureCopy.mipLevel = level;
textureCopy.origin = {};
textureCopy.aspect = Aspect::Color;
TexelCopyBufferLayout dataLayout;
dataLayout.offset = 0;
dataLayout.bytesPerRow = bytesPerRow;
dataLayout.rowsPerImage = largestMipSize.height / blockInfo.height;
Extent3D mipSize = GetMipLevelSingleSubresourcePhysicalSize(level, Aspect::Color);
for (uint32_t layer = range.baseArrayLayer;
layer < range.baseArrayLayer + range.layerCount; ++layer) {
if (clearValue == TextureBase::ClearValue::Zero &&
IsSubresourceContentInitialized(
SubresourceRange::SingleMipAndLayer(level, layer, Aspect::Color))) {
// Skip lazy clears if already initialized.
continue;
}
textureCopy.origin.z = TexelCount{layer};
DAWN_TRY(DoTexSubImage(gl, textureCopy, 0, dataLayout, mipSize));
}
}
DAWN_GL_TRY(gl, BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
}
if (clearValue == TextureBase::ClearValue::Zero) {
SetIsSubresourceContentInitialized(true, range);
device->IncrementLazyClearCountForTesting();
}
return {};
}
MaybeError Texture::EnsureSubresourceContentInitialized(const OpenGLFunctions& gl,
const SubresourceRange& range) {
if (!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
return {};
}
if (!IsSubresourceContentInitialized(range)) {
DAWN_TRY(ClearTexture(gl, range, TextureBase::ClearValue::Zero));
}
return {};
}
MaybeError Texture::SynchronizeTextureBeforeUse() {
SharedTextureMemoryBase::PendingFenceList fences;
SharedResourceMemoryContents* contents = GetSharedResourceMemoryContents();
if (contents != nullptr) {
contents->AcquirePendingFences(&fences);
}
for (const auto& fenceAndSignaledValue : fences) {
SharedFence* fence = ToBackend(fenceAndSignaledValue.object).Get();
DAWN_TRY(fence->ServerWait(fenceAndSignaledValue.signaledValue));
}
mLastSharedTextureMemoryUsageSerial = GetDevice()->GetQueue()->GetPendingCommandSerial();
return {};
}
// TextureView
// static
ResultOrError<Ref<TextureView>> TextureView::Create(
TextureBase* texture,
const UnpackedPtr<TextureViewDescriptor>& descriptor) {
// Wrap the handle in a TextureView class early so that it is deleted if initialization fails
Ref<TextureView> view = AcquireRef(new TextureView(texture, descriptor));
if (RequiresCreatingNewTextureView(texture, descriptor)) {
view->mOwnsHandle = OwnsHandle::Yes;
} else {
view->mOwnsHandle = OwnsHandle::No;
}
DAWN_TRY(ToBackend(texture->GetDevice())
->EnqueueGL([view, texture = Ref<Texture>(ToBackend(texture))](
const OpenGLFunctions& gl) -> MaybeError {
if (texture->IsDestroyed()) {
view->mHandle = 0;
} else if (view->mOwnsHandle == OwnsHandle::Yes) {
GLuint handle = 0;
DAWN_ASSERT(gl.IsAtLeastGL(4, 3));
DAWN_GL_TRY(gl, GenTextures(1, &handle));
DAWN_GL_TRY(gl,
TextureView(handle, view->GetGLTarget(), texture->GetHandle(),
view->GetInternalFormat(), view->GetBaseMipLevel(),
view->GetLevelCount(), view->GetBaseArrayLayer(),
view->GetLayerCount()));
view->mHandle = handle;
} else {
view->mHandle = texture->GetHandle();
}
return {};
}));
return view;
}
TextureView::TextureView(TextureBase* texture, const UnpackedPtr<TextureViewDescriptor>& descriptor)
: TextureViewBase(texture, descriptor) {
mTarget = TargetForTextureViewDimension(descriptor->dimension, texture->GetSampleCount());
}
TextureView::~TextureView() {}
void TextureView::DestroyImpl(DestroyReason reason) {
TextureViewBase::DestroyImpl(reason);
if (mOwnsHandle == OwnsHandle::Yes) {
IgnoreErrors(
ToBackend(GetDevice())
->EnqueueDestroyGL(this, &TextureView::GetHandle, reason,
[](const OpenGLFunctions& gl, GLuint handle) -> MaybeError {
DAWN_GL_TRY_IGNORE_ERRORS(gl, DeleteTextures(1, &handle));
return {};
}));
}
}
GLuint TextureView::GetHandle() const {
DAWN_ASSERT(mHandle != 0);
return mHandle;
}
GLenum TextureView::GetGLTarget() const {
return mTarget;
}
MaybeError TextureView::BindToFramebuffer(const OpenGLFunctions& gl,
GLenum target,
GLenum attachment,
GLuint depthSlice) {
DAWN_ASSERT(depthSlice <
static_cast<GLuint>(GetSingleSubresourceVirtualSize().depthOrArrayLayers));
// Use the base texture where possible to minimize the amount of copying required on GLES.
bool useOwnView = GetFormat().format != GetTexture()->GetFormat().format &&
!GetTexture()->GetFormat().HasDepthOrStencil();
GLenum textarget;
GLuint textureHandle, mipLevel, arrayLayer;
if (useOwnView) {
// Use our own texture handle and target which points to a subset of the texture's
// subresources.
textureHandle = GetHandle();
textarget = GetGLTarget();
mipLevel = 0;
arrayLayer = 0;
} else {
// Use the texture's handle and target, with the view's base mip level and base array
textureHandle = ToBackend(GetTexture())->GetHandle();
textarget = ToBackend(GetTexture())->GetGLTarget();
mipLevel = GetBaseMipLevel();
// We have validated that the depthSlice in render pass's colorAttachments must be undefined
// for 2d RTVs, which value is set to 0. For 3d RTVs, the baseArrayLayer must be 0. So here
// we can simply use baseArrayLayer + depthSlice to specify the slice in RTVs without
// checking the view's dimension.
arrayLayer = GetBaseArrayLayer() + depthSlice;
}
DAWN_ASSERT(textureHandle != 0);
return FramebufferTextureHelper(gl, textarget, target, attachment, textureHandle, mipLevel,
arrayLayer);
}
GLenum TextureView::GetInternalFormat() const {
// Depth/stencil don't support reinterpretation, and the aspect is specified at
// bind time. In that case, we use the base texture format.
const Format& format =
GetFormat().HasDepthOrStencil() ? GetTexture()->GetFormat() : GetFormat();
const GLFormat& glFormat = ToBackend(GetDevice())->GetGLFormat(format);
return glFormat.internalFormat;
}
} // namespace dawn::native::opengl