mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-08-03 02:35:55 +00:00
Bug: dawn:1336 Change-Id: I1fd189bd6e3689df6f10351e8ba19fee569bda23 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/122023 Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Loko Kung <lokokung@google.com> Reviewed-by: Austin Eng <enga@chromium.org>
687 lines
28 KiB
C++
687 lines
28 KiB
C++
// Copyright 2017 The Dawn Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include "dawn/native/opengl/TextureGL.h"
|
|
|
|
#include <limits>
|
|
#include <utility>
|
|
|
|
#include "dawn/common/Assert.h"
|
|
#include "dawn/common/Constants.h"
|
|
#include "dawn/common/Math.h"
|
|
#include "dawn/native/EnumMaskIterator.h"
|
|
#include "dawn/native/opengl/BufferGL.h"
|
|
#include "dawn/native/opengl/CommandBufferGL.h"
|
|
#include "dawn/native/opengl/DeviceGL.h"
|
|
#include "dawn/native/opengl/UtilsGL.h"
|
|
|
|
namespace dawn::native::opengl {
|
|
|
|
namespace {
|
|
|
|
GLenum TargetForTexture(const TextureDescriptor* descriptor) {
|
|
switch (descriptor->dimension) {
|
|
case wgpu::TextureDimension::e1D:
|
|
case wgpu::TextureDimension::e2D:
|
|
if (descriptor->size.depthOrArrayLayers > 1) {
|
|
ASSERT(descriptor->sampleCount == 1);
|
|
return GL_TEXTURE_2D_ARRAY;
|
|
} else {
|
|
if (descriptor->sampleCount > 1) {
|
|
return GL_TEXTURE_2D_MULTISAMPLE;
|
|
} else {
|
|
return GL_TEXTURE_2D;
|
|
}
|
|
}
|
|
case wgpu::TextureDimension::e3D:
|
|
ASSERT(descriptor->sampleCount == 1);
|
|
return GL_TEXTURE_3D;
|
|
}
|
|
UNREACHABLE();
|
|
}
|
|
|
|
GLenum TargetForTextureViewDimension(wgpu::TextureViewDimension dimension,
|
|
uint32_t arrayLayerCount,
|
|
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) {
|
|
ASSERT(arrayLayerCount == 1);
|
|
return GL_TEXTURE_2D_MULTISAMPLE;
|
|
}
|
|
ASSERT(sampleCount == 1);
|
|
return GL_TEXTURE_2D_ARRAY;
|
|
case wgpu::TextureViewDimension::Cube:
|
|
ASSERT(sampleCount == 1);
|
|
ASSERT(arrayLayerCount == 6);
|
|
return GL_TEXTURE_CUBE_MAP;
|
|
case wgpu::TextureViewDimension::CubeArray:
|
|
ASSERT(sampleCount == 1);
|
|
ASSERT(arrayLayerCount % 6 == 0);
|
|
return GL_TEXTURE_CUBE_MAP_ARRAY;
|
|
case wgpu::TextureViewDimension::e3D:
|
|
return GL_TEXTURE_3D;
|
|
|
|
case wgpu::TextureViewDimension::Undefined:
|
|
break;
|
|
}
|
|
UNREACHABLE();
|
|
}
|
|
|
|
bool RequiresCreatingNewTextureView(const TextureBase* texture,
|
|
const TextureViewDescriptor* textureViewDescriptor) {
|
|
constexpr wgpu::TextureUsage kShaderUsageNeedsView =
|
|
wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::TextureBinding;
|
|
constexpr wgpu::TextureUsage kUsageNeedsView =
|
|
kShaderUsageNeedsView | wgpu::TextureUsage::RenderAttachment;
|
|
if ((texture->GetInternalUsage() & kUsageNeedsView) == 0) {
|
|
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) == 0) {
|
|
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 (texture->GetNumMipLevels() != textureViewDescriptor->mipLevelCount) {
|
|
return true;
|
|
}
|
|
|
|
if (ToBackend(texture)->GetGLFormat().format == GL_DEPTH_STENCIL &&
|
|
(texture->GetUsage() & wgpu::TextureUsage::TextureBinding) != 0 &&
|
|
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;
|
|
}
|
|
|
|
switch (textureViewDescriptor->dimension) {
|
|
case wgpu::TextureViewDimension::Cube:
|
|
case wgpu::TextureViewDimension::CubeArray:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void AllocateTexture(const OpenGLFunctions& gl,
|
|
GLenum target,
|
|
GLsizei samples,
|
|
GLuint levels,
|
|
GLenum internalFormat,
|
|
const Extent3D& size) {
|
|
// 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
|
|
switch (target) {
|
|
case GL_TEXTURE_2D_ARRAY:
|
|
case GL_TEXTURE_3D:
|
|
gl.TexStorage3D(target, levels, internalFormat, size.width, size.height,
|
|
size.depthOrArrayLayers);
|
|
break;
|
|
case GL_TEXTURE_2D:
|
|
case GL_TEXTURE_CUBE_MAP:
|
|
gl.TexStorage2D(target, levels, internalFormat, size.width, size.height);
|
|
break;
|
|
case GL_TEXTURE_2D_MULTISAMPLE:
|
|
gl.TexStorage2DMultisample(target, samples, internalFormat, size.width, size.height,
|
|
true);
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// Texture
|
|
|
|
// static
|
|
ResultOrError<Ref<Texture>> Texture::Create(Device* device, const TextureDescriptor* descriptor) {
|
|
Ref<Texture> texture = AcquireRef(new Texture(device, descriptor));
|
|
if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) {
|
|
DAWN_TRY(
|
|
texture->ClearTexture(texture->GetAllSubresources(), TextureBase::ClearValue::NonZero));
|
|
}
|
|
return std::move(texture);
|
|
}
|
|
|
|
Texture::Texture(Device* device, const TextureDescriptor* descriptor)
|
|
: Texture(device, descriptor, 0, TextureState::OwnedInternal) {
|
|
const OpenGLFunctions& gl = device->GetGL();
|
|
|
|
gl.GenTextures(1, &mHandle);
|
|
uint32_t levels = GetNumMipLevels();
|
|
|
|
const GLFormat& glFormat = GetGLFormat();
|
|
|
|
gl.BindTexture(mTarget, mHandle);
|
|
|
|
AllocateTexture(gl, mTarget, GetSampleCount(), levels, glFormat.internalFormat, GetSize());
|
|
|
|
// The texture is not complete if it uses mipmapping and not all levels up to
|
|
// MAX_LEVEL have been defined.
|
|
gl.TexParameteri(mTarget, GL_TEXTURE_MAX_LEVEL, levels - 1);
|
|
}
|
|
|
|
void Texture::Touch() {
|
|
mGenID++;
|
|
}
|
|
|
|
uint32_t Texture::GetGenID() const {
|
|
return mGenID;
|
|
}
|
|
|
|
Texture::Texture(Device* device,
|
|
const TextureDescriptor* descriptor,
|
|
GLuint handle,
|
|
TextureState state)
|
|
: TextureBase(device, descriptor, state), mHandle(handle) {
|
|
mTarget = TargetForTexture(descriptor);
|
|
}
|
|
|
|
Texture::~Texture() {}
|
|
|
|
void Texture::DestroyImpl() {
|
|
TextureBase::DestroyImpl();
|
|
if (GetTextureState() == TextureState::OwnedInternal) {
|
|
const OpenGLFunctions& gl = ToBackend(GetDevice())->GetGL();
|
|
gl.DeleteTextures(1, &mHandle);
|
|
mHandle = 0;
|
|
}
|
|
}
|
|
|
|
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 SubresourceRange& range,
|
|
TextureBase::ClearValue clearValue) {
|
|
// TODO(crbug.com/dawn/850): initialize the textures with compressed formats.
|
|
if (GetFormat().isCompressed) {
|
|
return {};
|
|
}
|
|
|
|
Device* device = ToBackend(GetDevice());
|
|
const OpenGLFunctions& gl = device->GetGL();
|
|
|
|
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) {
|
|
gl.DepthMask(GL_TRUE);
|
|
}
|
|
if (range.aspects & Aspect::Stencil) {
|
|
gl.StencilMask(GetStencilMaskFromStencilFormat(GetFormat().format));
|
|
}
|
|
|
|
auto DoClear = [&](Aspect aspects) {
|
|
if (aspects == (Aspect::Depth | Aspect::Stencil)) {
|
|
gl.ClearBufferfi(GL_DEPTH_STENCIL, 0, depth, stencil);
|
|
} else if (aspects == Aspect::Depth) {
|
|
gl.ClearBufferfv(GL_DEPTH, 0, &depth);
|
|
} else if (aspects == Aspect::Stencil) {
|
|
gl.ClearBufferiv(GL_STENCIL, 0, &stencil);
|
|
} else {
|
|
UNREACHABLE();
|
|
}
|
|
};
|
|
|
|
GLuint framebuffer = 0;
|
|
gl.GenFramebuffers(1, &framebuffer);
|
|
gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer);
|
|
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 {
|
|
UNREACHABLE();
|
|
}
|
|
|
|
for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
|
|
++level) {
|
|
switch (GetDimension()) {
|
|
case wgpu::TextureDimension::e1D:
|
|
case wgpu::TextureDimension::e2D:
|
|
if (GetArrayLayers() == 1) {
|
|
Aspect aspectsToClear = Aspect::None;
|
|
for (Aspect aspect : IterateEnumMask(range.aspects)) {
|
|
if (clearValue == TextureBase::ClearValue::Zero &&
|
|
IsSubresourceContentInitialized(
|
|
SubresourceRange::SingleMipAndLayer(level, 0, aspect))) {
|
|
// Skip lazy clears if already initialized.
|
|
continue;
|
|
}
|
|
aspectsToClear |= aspect;
|
|
}
|
|
|
|
if (aspectsToClear == Aspect::None) {
|
|
continue;
|
|
}
|
|
gl.FramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, GetGLTarget(),
|
|
GetHandle(), static_cast<GLint>(level));
|
|
DoClear(aspectsToClear);
|
|
} else {
|
|
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;
|
|
}
|
|
|
|
gl.FramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attachment,
|
|
GetHandle(), static_cast<GLint>(level),
|
|
static_cast<GLint>(layer));
|
|
DoClear(aspectsToClear);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case wgpu::TextureDimension::e3D:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
gl.Enable(GL_SCISSOR_TEST);
|
|
gl.DeleteFramebuffers(1, &framebuffer);
|
|
} else {
|
|
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;
|
|
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};
|
|
|
|
wgpu::TextureComponentType baseType = GetFormat().GetAspectInfo(Aspect::Color).baseType;
|
|
|
|
const GLFormat& glFormat = GetGLFormat();
|
|
for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount;
|
|
++level) {
|
|
Extent3D mipSize = GetMipLevelSingleSubresourcePhysicalSize(level);
|
|
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)) {
|
|
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;
|
|
gl.GenFramebuffers(1, &framebuffer);
|
|
gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer);
|
|
|
|
GLenum attachment = GL_COLOR_ATTACHMENT0;
|
|
gl.DrawBuffers(1, &attachment);
|
|
|
|
gl.Disable(GL_SCISSOR_TEST);
|
|
gl.ColorMask(true, true, true, true);
|
|
|
|
auto DoClear = [&]() {
|
|
switch (baseType) {
|
|
case wgpu::TextureComponentType::Float: {
|
|
gl.ClearBufferfv(GL_COLOR, 0,
|
|
clearValue == TextureBase::ClearValue::Zero
|
|
? kClearColorDataFloat0.data()
|
|
: kClearColorDataFloat1.data());
|
|
break;
|
|
}
|
|
case wgpu::TextureComponentType::Uint: {
|
|
gl.ClearBufferuiv(GL_COLOR, 0,
|
|
clearValue == TextureBase::ClearValue::Zero
|
|
? kClearColorDataUint0.data()
|
|
: kClearColorDataUint1.data());
|
|
break;
|
|
}
|
|
case wgpu::TextureComponentType::Sint: {
|
|
gl.ClearBufferiv(GL_COLOR, 0,
|
|
reinterpret_cast<const GLint*>(
|
|
clearValue == TextureBase::ClearValue::Zero
|
|
? kClearColorDataUint0.data()
|
|
: kClearColorDataUint1.data()));
|
|
break;
|
|
}
|
|
|
|
case wgpu::TextureComponentType::DepthComparison:
|
|
UNREACHABLE();
|
|
}
|
|
};
|
|
|
|
if (GetArrayLayers() == 1) {
|
|
switch (GetDimension()) {
|
|
case wgpu::TextureDimension::e1D:
|
|
case wgpu::TextureDimension::e2D:
|
|
gl.FramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment,
|
|
GetGLTarget(), GetHandle(), level);
|
|
DoClear();
|
|
break;
|
|
case wgpu::TextureDimension::e3D:
|
|
uint32_t depth = GetMipLevelSingleSubresourceVirtualSize(level)
|
|
.depthOrArrayLayers;
|
|
for (GLint z = 0; z < static_cast<GLint>(depth); ++z) {
|
|
gl.FramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attachment,
|
|
GetHandle(), level, z);
|
|
DoClear();
|
|
}
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
ASSERT(GetDimension() == wgpu::TextureDimension::e2D);
|
|
gl.FramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, attachment, GetHandle(),
|
|
level, layer);
|
|
DoClear();
|
|
}
|
|
|
|
gl.Enable(GL_SCISSOR_TEST);
|
|
gl.DeleteFramebuffers(1, &framebuffer);
|
|
gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
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;
|
|
ASSERT(kTextureBytesPerRowAlignment % blockInfo.byteSize == 0);
|
|
|
|
Extent3D largestMipSize = GetMipLevelSingleSubresourcePhysicalSize(range.baseMipLevel);
|
|
uint32_t bytesPerRow =
|
|
Align((largestMipSize.width / blockInfo.width) * blockInfo.byteSize, 4);
|
|
|
|
// Make sure that we are not rounding
|
|
ASSERT(bytesPerRow % blockInfo.byteSize == 0);
|
|
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());
|
|
|
|
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;
|
|
|
|
TextureDataLayout dataLayout;
|
|
dataLayout.offset = 0;
|
|
dataLayout.bytesPerRow = bytesPerRow;
|
|
dataLayout.rowsPerImage = largestMipSize.height;
|
|
|
|
Extent3D mipSize = GetMipLevelSingleSubresourcePhysicalSize(level);
|
|
|
|
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 = layer;
|
|
DoTexSubImage(gl, textureCopy, 0, dataLayout, mipSize);
|
|
}
|
|
}
|
|
gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
|
}
|
|
if (clearValue == TextureBase::ClearValue::Zero) {
|
|
SetIsSubresourceContentInitialized(true, range);
|
|
device->IncrementLazyClearCountForTesting();
|
|
}
|
|
Touch();
|
|
return {};
|
|
}
|
|
|
|
MaybeError Texture::EnsureSubresourceContentInitialized(const SubresourceRange& range) {
|
|
if (!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) {
|
|
return {};
|
|
}
|
|
if (!IsSubresourceContentInitialized(range)) {
|
|
DAWN_TRY(ClearTexture(range, TextureBase::ClearValue::Zero));
|
|
}
|
|
return {};
|
|
}
|
|
|
|
// TextureView
|
|
|
|
TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor)
|
|
: TextureViewBase(texture, descriptor), mOwnsHandle(false) {
|
|
mTarget = TargetForTextureViewDimension(descriptor->dimension, descriptor->arrayLayerCount,
|
|
texture->GetSampleCount());
|
|
|
|
// Texture could be destroyed by the time we make a view.
|
|
if (GetTexture()->GetTextureState() == Texture::TextureState::Destroyed) {
|
|
return;
|
|
}
|
|
|
|
if (!RequiresCreatingNewTextureView(texture, descriptor)) {
|
|
mHandle = ToBackend(texture)->GetHandle();
|
|
} else {
|
|
const OpenGLFunctions& gl = ToBackend(GetDevice())->GetGL();
|
|
if (gl.IsAtLeastGL(4, 3)) {
|
|
gl.GenTextures(1, &mHandle);
|
|
const Texture* textureGL = ToBackend(texture);
|
|
gl.TextureView(mHandle, mTarget, textureGL->GetHandle(), GetInternalFormat(),
|
|
descriptor->baseMipLevel, descriptor->mipLevelCount,
|
|
descriptor->baseArrayLayer, descriptor->arrayLayerCount);
|
|
mOwnsHandle = true;
|
|
} else {
|
|
// Simulate glTextureView() with texture-to-texture copies.
|
|
mUseCopy = true;
|
|
mHandle = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
TextureView::~TextureView() {}
|
|
|
|
void TextureView::DestroyImpl() {
|
|
TextureViewBase::DestroyImpl();
|
|
if (mOwnsHandle) {
|
|
const OpenGLFunctions& gl = ToBackend(GetDevice())->GetGL();
|
|
gl.DeleteTextures(1, &mHandle);
|
|
}
|
|
}
|
|
|
|
GLuint TextureView::GetHandle() const {
|
|
ASSERT(mHandle != 0);
|
|
return mHandle;
|
|
}
|
|
|
|
GLenum TextureView::GetGLTarget() const {
|
|
return mTarget;
|
|
}
|
|
|
|
void TextureView::BindToFramebuffer(GLenum target, GLenum attachment) {
|
|
const OpenGLFunctions& gl = ToBackend(GetDevice())->GetGL();
|
|
|
|
// 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();
|
|
|
|
GLuint handle, textarget, mipLevel, arrayLayer;
|
|
if (useOwnView) {
|
|
// Use our own texture handle and target which points to a subset of the texture's
|
|
// subresources.
|
|
handle = 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
|
|
|
|
handle = ToBackend(GetTexture())->GetHandle();
|
|
textarget = ToBackend(GetTexture())->GetGLTarget();
|
|
mipLevel = GetBaseMipLevel();
|
|
arrayLayer = GetBaseArrayLayer();
|
|
}
|
|
|
|
ASSERT(handle != 0);
|
|
if (textarget == GL_TEXTURE_2D_ARRAY || textarget == GL_TEXTURE_3D) {
|
|
gl.FramebufferTextureLayer(target, attachment, handle, mipLevel, arrayLayer);
|
|
} else {
|
|
gl.FramebufferTexture2D(target, attachment, textarget, handle, mipLevel);
|
|
}
|
|
}
|
|
|
|
void TextureView::CopyIfNeeded() {
|
|
if (!mUseCopy) {
|
|
return;
|
|
}
|
|
|
|
const Texture* texture = ToBackend(GetTexture());
|
|
if (mGenID == texture->GetGenID()) {
|
|
return;
|
|
}
|
|
|
|
Device* device = ToBackend(GetDevice());
|
|
const OpenGLFunctions& gl = device->GetGL();
|
|
uint32_t srcLevel = GetBaseMipLevel();
|
|
uint32_t numLevels = GetLevelCount();
|
|
|
|
uint32_t width = texture->GetWidth() >> srcLevel;
|
|
uint32_t height = texture->GetHeight() >> srcLevel;
|
|
Extent3D size{width, height, GetLayerCount()};
|
|
|
|
if (mHandle == 0) {
|
|
gl.GenTextures(1, &mHandle);
|
|
gl.BindTexture(mTarget, mHandle);
|
|
AllocateTexture(gl, mTarget, texture->GetSampleCount(), numLevels, GetInternalFormat(),
|
|
size);
|
|
mOwnsHandle = true;
|
|
}
|
|
|
|
Origin3D src{0, 0, GetBaseArrayLayer()};
|
|
Origin3D dst{0, 0, 0};
|
|
for (GLuint level = 0; level < numLevels; ++level) {
|
|
CopyImageSubData(gl, GetAspects(), texture->GetHandle(), texture->GetGLTarget(),
|
|
srcLevel + level, src, mHandle, mTarget, level, dst, size);
|
|
}
|
|
|
|
mGenID = texture->GetGenID();
|
|
}
|
|
|
|
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
|