blob: 062450948881aa1e1acd913e44a1da9180ebee66 [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/CommandBufferGL.h"
#include <algorithm>
#include <cstring>
#include <utility>
#include <vector>
#include "dawn/common/MatchVariant.h"
#include "dawn/common/Range.h"
#include "dawn/native/BindGroup.h"
#include "dawn/native/BindGroupTracker.h"
#include "dawn/native/CommandEncoder.h"
#include "dawn/native/Commands.h"
#include "dawn/native/ExternalTexture.h"
#include "dawn/native/ImmediateConstantsTracker.h"
#include "dawn/native/RenderBundle.h"
#include "dawn/native/opengl/BufferGL.h"
#include "dawn/native/opengl/ComputePipelineGL.h"
#include "dawn/native/opengl/DeviceGL.h"
#include "dawn/native/opengl/Forward.h"
#include "dawn/native/opengl/PersistentPipelineStateGL.h"
#include "dawn/native/opengl/PhysicalDeviceGL.h"
#include "dawn/native/opengl/PipelineLayoutGL.h"
#include "dawn/native/opengl/QuerySetGL.h"
#include "dawn/native/opengl/RenderPipelineGL.h"
#include "dawn/native/opengl/SamplerGL.h"
#include "dawn/native/opengl/TextureGL.h"
#include "dawn/native/opengl/UtilsGL.h"
#include "partition_alloc/pointers/raw_ptr.h"
namespace dawn::native::opengl {
namespace {
GLenum ComponentSwizzle(wgpu::ComponentSwizzle swizzle) {
switch (swizzle) {
case wgpu::ComponentSwizzle::Zero:
return GL_ZERO;
case wgpu::ComponentSwizzle::One:
return GL_ONE;
case wgpu::ComponentSwizzle::R:
return GL_RED;
case wgpu::ComponentSwizzle::G:
return GL_GREEN;
case wgpu::ComponentSwizzle::B:
return GL_BLUE;
case wgpu::ComponentSwizzle::A:
return GL_ALPHA;
case wgpu::ComponentSwizzle::Undefined:
DAWN_UNREACHABLE();
}
}
GLenum IndexFormatType(wgpu::IndexFormat format) {
switch (format) {
case wgpu::IndexFormat::Uint16:
return GL_UNSIGNED_SHORT;
case wgpu::IndexFormat::Uint32:
return GL_UNSIGNED_INT;
case wgpu::IndexFormat::Undefined:
break;
}
DAWN_UNREACHABLE();
}
GLenum VertexFormatType(wgpu::VertexFormat format) {
switch (format) {
case wgpu::VertexFormat::Uint8:
case wgpu::VertexFormat::Uint8x2:
case wgpu::VertexFormat::Uint8x4:
case wgpu::VertexFormat::Unorm8:
case wgpu::VertexFormat::Unorm8x2:
case wgpu::VertexFormat::Unorm8x4:
case wgpu::VertexFormat::Unorm8x4BGRA:
return GL_UNSIGNED_BYTE;
case wgpu::VertexFormat::Sint8:
case wgpu::VertexFormat::Sint8x2:
case wgpu::VertexFormat::Sint8x4:
case wgpu::VertexFormat::Snorm8:
case wgpu::VertexFormat::Snorm8x2:
case wgpu::VertexFormat::Snorm8x4:
return GL_BYTE;
case wgpu::VertexFormat::Uint16:
case wgpu::VertexFormat::Uint16x2:
case wgpu::VertexFormat::Uint16x4:
case wgpu::VertexFormat::Unorm16:
case wgpu::VertexFormat::Unorm16x2:
case wgpu::VertexFormat::Unorm16x4:
return GL_UNSIGNED_SHORT;
case wgpu::VertexFormat::Sint16:
case wgpu::VertexFormat::Sint16x2:
case wgpu::VertexFormat::Sint16x4:
case wgpu::VertexFormat::Snorm16:
case wgpu::VertexFormat::Snorm16x2:
case wgpu::VertexFormat::Snorm16x4:
return GL_SHORT;
case wgpu::VertexFormat::Float16:
case wgpu::VertexFormat::Float16x2:
case wgpu::VertexFormat::Float16x4:
return GL_HALF_FLOAT;
case wgpu::VertexFormat::Float32:
case wgpu::VertexFormat::Float32x2:
case wgpu::VertexFormat::Float32x3:
case wgpu::VertexFormat::Float32x4:
return GL_FLOAT;
case wgpu::VertexFormat::Uint32:
case wgpu::VertexFormat::Uint32x2:
case wgpu::VertexFormat::Uint32x3:
case wgpu::VertexFormat::Uint32x4:
return GL_UNSIGNED_INT;
case wgpu::VertexFormat::Sint32:
case wgpu::VertexFormat::Sint32x2:
case wgpu::VertexFormat::Sint32x3:
case wgpu::VertexFormat::Sint32x4:
return GL_INT;
case wgpu::VertexFormat::Unorm10_10_10_2:
return GL_UNSIGNED_INT_2_10_10_10_REV;
default:
DAWN_UNREACHABLE();
}
}
GLboolean VertexFormatIsNormalized(wgpu::VertexFormat format) {
switch (format) {
case wgpu::VertexFormat::Unorm8:
case wgpu::VertexFormat::Unorm8x2:
case wgpu::VertexFormat::Unorm8x4:
case wgpu::VertexFormat::Unorm8x4BGRA:
case wgpu::VertexFormat::Snorm8:
case wgpu::VertexFormat::Snorm8x2:
case wgpu::VertexFormat::Snorm8x4:
case wgpu::VertexFormat::Unorm16:
case wgpu::VertexFormat::Unorm16x2:
case wgpu::VertexFormat::Unorm16x4:
case wgpu::VertexFormat::Snorm16:
case wgpu::VertexFormat::Snorm16x2:
case wgpu::VertexFormat::Snorm16x4:
case wgpu::VertexFormat::Unorm10_10_10_2:
return GL_TRUE;
default:
return GL_FALSE;
}
}
bool VertexFormatIsInt(wgpu::VertexFormat format) {
switch (format) {
case wgpu::VertexFormat::Uint8:
case wgpu::VertexFormat::Uint8x2:
case wgpu::VertexFormat::Uint8x4:
case wgpu::VertexFormat::Sint8:
case wgpu::VertexFormat::Sint8x2:
case wgpu::VertexFormat::Sint8x4:
case wgpu::VertexFormat::Uint16:
case wgpu::VertexFormat::Uint16x2:
case wgpu::VertexFormat::Uint16x4:
case wgpu::VertexFormat::Sint16:
case wgpu::VertexFormat::Sint16x2:
case wgpu::VertexFormat::Sint16x4:
case wgpu::VertexFormat::Uint32:
case wgpu::VertexFormat::Uint32x2:
case wgpu::VertexFormat::Uint32x3:
case wgpu::VertexFormat::Uint32x4:
case wgpu::VertexFormat::Sint32:
case wgpu::VertexFormat::Sint32x2:
case wgpu::VertexFormat::Sint32x3:
case wgpu::VertexFormat::Sint32x4:
return true;
default:
return false;
}
}
// Vertex buffers and index buffers are implemented as part of an OpenGL VAO that
// corresponds to a VertexState. On the contrary in Dawn they are part of the global state.
// This means that we have to re-apply these buffers on a VertexState change.
class VertexStateBufferBindingTracker {
public:
void OnSetIndexBuffer(BufferBase* buffer) {
mIndexBufferDirty = true;
mIndexBuffer = ToBackend(buffer);
}
void OnSetVertexBuffer(VertexBufferSlot slot, BufferBase* buffer, uint64_t offset) {
mVertexBuffers[slot] = ToBackend(buffer);
mVertexBufferOffsets[slot] = offset;
mDirtyVertexBuffers.set(slot);
}
void OnSetPipeline(RenderPipelineBase* pipeline) {
if (mLastPipeline == pipeline) {
return;
}
mIndexBufferDirty = true;
mDirtyVertexBuffers |= pipeline->GetVertexBuffersUsed();
mLastPipeline = pipeline;
}
MaybeError Apply(const OpenGLFunctions& gl, int32_t baseVertex, uint32_t firstInstance) {
if (mBaseVertex != baseVertex) {
mBaseVertex = baseVertex;
mDirtyVertexBuffers |= mLastPipeline->GetVertexBuffersUsedAsVertexBuffer();
}
if (mFirstInstance != firstInstance) {
mFirstInstance = firstInstance;
mDirtyVertexBuffers |= mLastPipeline->GetVertexBuffersUsedAsInstanceBuffer();
}
if (mIndexBufferDirty && mIndexBuffer != nullptr) {
DAWN_GL_TRY(gl, BindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer->GetHandle()));
mIndexBufferDirty = false;
}
for (VertexBufferSlot slot : mDirtyVertexBuffers & mLastPipeline->GetVertexBuffersUsed()) {
for (VertexAttributeLocation location :
ToBackend(mLastPipeline)->GetAttributesUsingVertexBuffer(slot)) {
const VertexAttributeInfo& attribute = mLastPipeline->GetAttribute(location);
GLuint attribIndex = static_cast<GLuint>(static_cast<uint8_t>(location));
GLuint buffer = mVertexBuffers[slot]->GetHandle();
intptr_t offset = mVertexBufferOffsets[slot];
const VertexBufferInfo& vertexBuffer = mLastPipeline->GetVertexBuffer(slot);
if (vertexBuffer.stepMode == wgpu::VertexStepMode::Vertex) {
offset += mBaseVertex * vertexBuffer.arrayStride;
} else if (vertexBuffer.stepMode == wgpu::VertexStepMode::Instance) {
offset += mFirstInstance * vertexBuffer.arrayStride;
}
uint32_t components = GetVertexFormatInfo(attribute.format).componentCount;
GLenum formatType = VertexFormatType(attribute.format);
GLboolean normalized = VertexFormatIsNormalized(attribute.format);
DAWN_GL_TRY(gl, BindBuffer(GL_ARRAY_BUFFER, buffer));
if (VertexFormatIsInt(attribute.format)) {
DAWN_GL_TRY(
gl, VertexAttribIPointer(
attribIndex, components, formatType, vertexBuffer.arrayStride,
reinterpret_cast<void*>(offset +
static_cast<intptr_t>(attribute.offset))));
} else {
DAWN_GL_TRY(gl, VertexAttribPointer(
attribIndex, components, formatType, normalized,
vertexBuffer.arrayStride,
reinterpret_cast<void*>(
offset + static_cast<intptr_t>(attribute.offset))));
}
}
}
mDirtyVertexBuffers.reset();
return {};
}
private:
bool mIndexBufferDirty = false;
raw_ptr<Buffer> mIndexBuffer = nullptr;
VertexBufferMask mDirtyVertexBuffers;
PerVertexBuffer<Buffer*> mVertexBuffers;
PerVertexBuffer<uint64_t> mVertexBufferOffsets;
int32_t mBaseVertex = 0;
uint32_t mFirstInstance = 0;
raw_ptr<RenderPipelineBase> mLastPipeline = nullptr;
};
// Tracking dirty byte range of a vector that needs to call bufferSubData
// to update to the internal uniform buffer of mPipeline. Range it represents: [begin, end)
struct VectorDirtyRangeInfo {
size_t begin;
size_t end;
};
class BindGroupTracker : public BindGroupTrackerBase<false> {
public:
void OnSetPipeline(RenderPipeline* pipeline) {
BindGroupTrackerBase::OnSetPipeline(pipeline);
mPipeline = pipeline;
ResetInternalUniformDataBindgroupAndDirtyRange();
}
void OnSetPipeline(ComputePipeline* pipeline) {
BindGroupTrackerBase::OnSetPipeline(pipeline);
mPipeline = pipeline;
ResetInternalUniformDataBindgroupAndDirtyRange();
}
MaybeError Apply(const OpenGLFunctions& gl) {
BeforeApply();
for (BindGroupIndex index : mDirtyBindGroupsObjectChangedOrIsDynamic) {
DAWN_TRY(ApplyBindGroup(gl, index, mBindGroups[index], GetDynamicOffsets(index)));
}
DAWN_TRY(ApplyInternalUniforms(gl));
DAWN_TRY(ApplyInternalArrayLengthUniforms(gl));
AfterApply();
return {};
}
private:
MaybeError BindSamplerAtIndex(const OpenGLFunctions& gl,
SamplerBase* s,
FlatBindingIndex samplerIndex) {
Sampler* sampler = ToBackend(s);
for (TextureUnit unit : mPipeline->GetTextureUnitsForSampler(samplerIndex)) {
DAWN_GL_TRY(gl, BindSampler(uint32_t(unit), sampler->GetHandle()));
}
return {};
}
MaybeError ApplyBindGroup(const OpenGLFunctions& gl,
BindGroupIndex groupIndex,
BindGroupBase* group,
const ityp::span<BindingIndex, uint32_t>& dynamicOffsets) {
const auto& indices = ToBackend(mPipelineLayout)->GetBindingIndexInfo()[groupIndex];
for (BindingIndex bindingIndex : Range(group->GetLayout()->GetBindingCount())) {
const BindingInfo& bindingInfo = group->GetLayout()->GetBindingInfo(bindingIndex);
DAWN_TRY(MatchVariant(
bindingInfo.bindingLayout,
[&](const BufferBindingInfo& layout) -> MaybeError {
BufferBinding binding = group->GetBindingAsBufferBinding(bindingIndex);
GLuint buffer = ToBackend(binding.buffer)->GetHandle();
FlatBindingIndex index = indices[bindingIndex];
GLuint offset = binding.offset;
if (layout.hasDynamicOffset) {
// Dynamic buffers are packed at the front of BindingIndices.
offset += uint64_t(dynamicOffsets[bindingIndex]);
}
GLenum target;
switch (layout.type) {
case wgpu::BufferBindingType::Uniform:
target = GL_UNIFORM_BUFFER;
// Round uniform buffer binding sizes up to a multiple of 16 bytes since
// Tint will polyfill them as array<vec4u, ...>.
binding.size = Align(binding.size, 16u);
break;
case wgpu::BufferBindingType::Storage:
case kInternalStorageBufferBinding:
case wgpu::BufferBindingType::ReadOnlyStorage:
case kInternalReadOnlyStorageBufferBinding:
target = GL_SHADER_STORAGE_BUFFER;
UpdateSSBOLengthUniformData(gl, binding.size, groupIndex, bindingIndex);
break;
case wgpu::BufferBindingType::BindingNotUsed:
case wgpu::BufferBindingType::Undefined:
DAWN_UNREACHABLE();
}
DAWN_GL_TRY(
gl, BindBufferRange(target, GLuint(index), buffer, offset, binding.size));
return {};
},
[&](const StaticSamplerBindingInfo& layout) -> MaybeError {
DAWN_TRY(BindSamplerAtIndex(gl, layout.sampler.Get(), indices[bindingIndex]));
return {};
},
[&](const SamplerBindingInfo&) -> MaybeError {
DAWN_TRY(BindSamplerAtIndex(gl, group->GetBindingAsSampler(bindingIndex),
indices[bindingIndex]));
return {};
},
[&](const TextureBindingInfo&) -> MaybeError {
TextureView* view = ToBackend(group->GetBindingAsTextureView(bindingIndex));
GLuint handle = view->GetHandle();
GLenum target = view->GetGLTarget();
FlatBindingIndex viewIndex = indices[bindingIndex];
for (auto unit : mPipeline->GetTextureUnitsForTextureView(viewIndex)) {
DAWN_GL_TRY(gl, ActiveTexture(GL_TEXTURE0 + GLuint(unit)));
DAWN_GL_TRY(gl, BindTexture(target, handle));
if (ToBackend(view->GetTexture())->GetGLFormat().format ==
GL_DEPTH_STENCIL) {
Aspect aspect = view->GetAspects();
DAWN_ASSERT(HasOneBit(aspect));
switch (aspect) {
case Aspect::None:
case Aspect::Color:
case Aspect::CombinedDepthStencil:
case Aspect::Plane0:
case Aspect::Plane1:
case Aspect::Plane2:
DAWN_UNREACHABLE();
case Aspect::Depth:
DAWN_GL_TRY(gl,
TexParameteri(target, GL_DEPTH_STENCIL_TEXTURE_MODE,
GL_DEPTH_COMPONENT));
break;
case Aspect::Stencil:
DAWN_GL_TRY(gl,
TexParameteri(target, GL_DEPTH_STENCIL_TEXTURE_MODE,
GL_STENCIL_INDEX));
break;
}
}
DAWN_GL_TRY(gl, TexParameteri(target, GL_TEXTURE_BASE_LEVEL,
view->GetBaseMipLevel()));
DAWN_GL_TRY(
gl, TexParameteri(target, GL_TEXTURE_MAX_LEVEL,
view->GetBaseMipLevel() + view->GetLevelCount() - 1));
PhysicalDeviceBase* device =
ToBackend(mPipelineLayout->GetDevice())->GetPhysicalDevice();
if (ToBackend(device)->SupportTextureComponentSwizzle()) {
wgpu::TextureComponentSwizzle swizzle = view->GetSwizzle();
if (view->GetTexture()->GetFormat().HasDepthOrStencil()) {
swizzle = ComposeSwizzle(kR001Swizzle, swizzle);
}
DAWN_GL_TRY(gl, TexParameteri(target, GL_TEXTURE_SWIZZLE_R,
ComponentSwizzle(swizzle.r)));
DAWN_GL_TRY(gl, TexParameteri(target, GL_TEXTURE_SWIZZLE_G,
ComponentSwizzle(swizzle.g)));
DAWN_GL_TRY(gl, TexParameteri(target, GL_TEXTURE_SWIZZLE_B,
ComponentSwizzle(swizzle.b)));
DAWN_GL_TRY(gl, TexParameteri(target, GL_TEXTURE_SWIZZLE_A,
ComponentSwizzle(swizzle.a)));
}
}
// Some texture builtin function data needs emulation to update into the
// internal uniform buffer.
UpdateTextureBuiltinsUniformData(gl, view, viewIndex);
return {};
},
[&](const StorageTextureBindingInfo& layout) -> MaybeError {
TextureView* view = ToBackend(group->GetBindingAsTextureView(bindingIndex));
Texture* texture = ToBackend(view->GetTexture());
GLuint handle = texture->GetHandle();
FlatBindingIndex imageIndex = indices[bindingIndex];
GLenum access;
switch (layout.access) {
case wgpu::StorageTextureAccess::WriteOnly:
access = GL_WRITE_ONLY;
break;
case wgpu::StorageTextureAccess::ReadWrite:
access = GL_READ_WRITE;
break;
case wgpu::StorageTextureAccess::ReadOnly:
access = GL_READ_ONLY;
break;
case wgpu::StorageTextureAccess::BindingNotUsed:
case wgpu::StorageTextureAccess::Undefined:
DAWN_UNREACHABLE();
}
// OpenGL ES only supports either binding a layer or the entire
// texture in glBindImageTexture().
GLboolean isLayered;
if (texture->GetArrayLayers() == view->GetLayerCount()) {
isLayered = GL_TRUE;
} else if (view->GetLayerCount() == 1) {
isLayered = GL_FALSE;
} else {
DAWN_UNREACHABLE();
}
DAWN_GL_TRY(
gl, BindImageTexture(GLuint(imageIndex), handle, view->GetBaseMipLevel(),
isLayered, view->GetBaseArrayLayer(), access,
texture->GetGLFormat().internalFormat));
return {};
},
[&](const TexelBufferBindingInfo&) -> MaybeError {
// OpenGL does not support texel buffers.
// TODO(crbug/382544164): Prototype texel buffer feature
DAWN_UNREACHABLE();
return {};
},
[](const InputAttachmentBindingInfo&) -> MaybeError { DAWN_UNREACHABLE(); },
[](const ExternalTextureBindingInfo&) -> MaybeError { DAWN_UNREACHABLE(); }));
}
return {};
}
void UpdateTextureBuiltinsUniformData(const OpenGLFunctions& gl,
const TextureView* view,
FlatBindingIndex textureIndex) {
const auto& builtinInfo = mPipeline->GetEmulatedTextureBuiltinInfo();
auto iter = builtinInfo.find(textureIndex);
if (iter == builtinInfo.end()) {
return;
}
// Update data by retrieving information from texture view object.
uint32_t data;
switch (iter->second.query) {
case TextureQuery::NumLevels:
data = view->GetLevelCount();
break;
case TextureQuery::NumSamples:
data = view->GetTexture()->GetSampleCount();
break;
}
const size_t offset = iter->second.index * sizeof(uint32_t);
if (offset >= mInternalUniformBufferData.size()) {
mInternalUniformBufferData.resize(offset + sizeof(uint32_t));
}
memcpy(mInternalUniformBufferData.data() + offset, &data, sizeof(uint32_t));
// Updating dirty range of the data vector
mDirtyRange.begin = std::min(mDirtyRange.begin, offset);
mDirtyRange.end = std::max(mDirtyRange.end, offset + sizeof(uint32_t));
}
void ResetInternalUniformDataDirtyRange() {
mDirtyRange = {mInternalUniformBufferData.size(), 0};
}
MaybeError ApplyInternalUniforms(const OpenGLFunctions& gl) {
if (!mPipeline->NeedsTextureBuiltinUniformBuffer()) {
return {};
}
if (mDirtyRange.begin >= mDirtyRange.end) {
// Early return if no dirty uniform range needs updating.
return {};
}
const Buffer* internalUniformBuffer =
ToBackend(mPipelineLayout->GetDevice())->GetInternalTextureBuiltinsUniformBuffer();
DAWN_ASSERT(internalUniformBuffer);
GLuint internalUniformBufferHandle = internalUniformBuffer->GetHandle();
DAWN_GL_TRY(
gl, BindBufferBase(
GL_UNIFORM_BUFFER,
GLuint(ToBackend(mPipelineLayout)->GetInternalTextureBuiltinsUniformBinding()),
internalUniformBufferHandle));
DAWN_GL_TRY(gl, BindBuffer(GL_UNIFORM_BUFFER, internalUniformBufferHandle));
DAWN_GL_TRY(gl, BufferSubData(GL_UNIFORM_BUFFER, mDirtyRange.begin,
mDirtyRange.end - mDirtyRange.begin,
mInternalUniformBufferData.data() + mDirtyRange.begin));
DAWN_GL_TRY(gl, BindBuffer(GL_UNIFORM_BUFFER, 0));
ResetInternalUniformDataDirtyRange();
return {};
}
MaybeError ApplyInternalArrayLengthUniforms(const OpenGLFunctions& gl) {
if (!mPipeline->NeedsSSBOLengthUniformBuffer()) {
return {};
}
if (mDirtyRangeArrayLength.begin >= mDirtyRangeArrayLength.end) {
// Early return if no dirty uniform range needs updating.
return {};
}
const Buffer* internalUniformBuffer =
ToBackend(mPipelineLayout->GetDevice())->GetInternalArrayLengthUniformBuffer();
DAWN_ASSERT(internalUniformBuffer);
GLuint internalUniformBufferHandle = internalUniformBuffer->GetHandle();
DAWN_GL_TRY(gl,
BindBufferBase(
GL_UNIFORM_BUFFER,
GLuint(ToBackend(mPipelineLayout)->GetInternalArrayLengthUniformBinding()),
internalUniformBufferHandle));
DAWN_GL_TRY(gl, BindBuffer(GL_UNIFORM_BUFFER, internalUniformBufferHandle));
DAWN_GL_TRY(
gl, BufferSubData(
GL_UNIFORM_BUFFER, sizeof(uint32_t) * mDirtyRangeArrayLength.begin,
sizeof(uint32_t) * (mDirtyRangeArrayLength.end - mDirtyRangeArrayLength.begin),
mInternalArrayLengthBufferData.data() + mDirtyRangeArrayLength.begin));
DAWN_GL_TRY(gl, BindBuffer(GL_UNIFORM_BUFFER, 0));
ResetInternalUniformDataDirtyRangeArrayLength();
return {};
}
void UpdateSSBOLengthUniformData(const OpenGLFunctions& gl,
uint64_t size,
BindGroupIndex groupIndex,
BindingIndex bindingIndex) {
if (!mPipeline->NeedsSSBOLengthUniformBuffer()) {
return;
}
const auto& bindingIndexInfo = ToBackend(mPipelineLayout)->GetBindingIndexInfo();
FlatBindingIndex ssboIndex = bindingIndexInfo[groupIndex][bindingIndex];
if (ssboIndex >= mInternalArrayLengthBufferData.size()) {
mInternalArrayLengthBufferData.resize(ssboIndex + FlatBindingIndex(4));
}
mInternalArrayLengthBufferData[ssboIndex] = static_cast<uint32_t>(size);
// Updating dirty range of the data vector
mDirtyRangeArrayLength.begin = std::min(mDirtyRangeArrayLength.begin, size_t(ssboIndex));
mDirtyRangeArrayLength.end = std::max(mDirtyRangeArrayLength.end, size_t(ssboIndex) + 1);
}
void ResetInternalUniformDataDirtyRangeArrayLength() {
mDirtyRangeArrayLength = {size_t(mInternalArrayLengthBufferData.size()), 0};
}
void ResetInternalUniformDataBindgroupAndDirtyRange() {
// Mark bind groups that need emulated builtin uniforms dirty so that they can be updated
// properly, even if the bind group is not updated.
// TODO(crbug.com/408065421): This forces bindgroups with metadata to be completely set
// again each pipeline change. In the future we will want to optimize that to only recompute
// the metadata as needed, not force a rebind of all resources.
const auto& builtinInfo = mPipeline->GetEmulatedTextureBuiltinInfo();
for (const auto& entry : builtinInfo) {
mDirtyBindGroupsObjectChangedOrIsDynamic.set(BindGroupIndex(entry.second.group));
}
ResetInternalUniformDataDirtyRange();
ResetInternalUniformDataDirtyRangeArrayLength();
}
raw_ptr<PipelineGL> mPipeline = nullptr;
// The data used for mPipeline's internal uniform buffer from current bind group.
// Expecting no more than 4 texture bindings called as textureNumLevels/textureNumSamples
// argument in a pipeline. Initialize the vector to this size to avoid frequent resizing.
std::vector<uint8_t> mInternalUniformBufferData = std::vector<uint8_t>(4 * sizeof(uint32_t));
// Tracking dirty byte range of the mInternalUniformBufferData that needs to call bufferSubData
// to update to the internal uniform buffer of mPipeline.
VectorDirtyRangeInfo mDirtyRange;
// The data used for mPipeline's internal uniform buffer to store ssbo buffer sizes.
ityp::vector<FlatBindingIndex, uint32_t> mInternalArrayLengthBufferData;
// Tracking dirty byte range of the mInternalArrayLengthBufferData that needs to call
// bufferSubData to update to the internal uniform buffer of mPipeline.
VectorDirtyRangeInfo mDirtyRangeArrayLength;
};
MaybeError ResolveMultisampledRenderTargets(const OpenGLFunctions& gl,
const BeginRenderPassCmd* renderPass) {
DAWN_ASSERT(renderPass != nullptr);
// Reset state that may affect glBlitFramebuffer().
DAWN_GL_TRY(gl, Disable(GL_SCISSOR_TEST));
GLuint readFbo = 0;
GLuint writeFbo = 0;
for (auto i : renderPass->attachmentState->GetColorAttachmentsMask()) {
if (renderPass->colorAttachments[i].resolveTarget != nullptr) {
if (readFbo == 0) {
DAWN_ASSERT(writeFbo == 0);
DAWN_GL_TRY(gl, GenFramebuffers(1, &readFbo));
DAWN_GL_TRY(gl, GenFramebuffers(1, &writeFbo));
}
TextureView* colorView = ToBackend(renderPass->colorAttachments[i].view.Get());
DAWN_GL_TRY(gl, BindFramebuffer(GL_READ_FRAMEBUFFER, readFbo));
DAWN_TRY(colorView->BindToFramebuffer(gl, GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0));
TextureView* resolveView =
ToBackend(renderPass->colorAttachments[i].resolveTarget.Get());
DAWN_GL_TRY(gl, BindFramebuffer(GL_DRAW_FRAMEBUFFER, writeFbo));
DAWN_TRY(resolveView->BindToFramebuffer(gl, GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0));
DAWN_GL_TRY(gl, BlitFramebuffer(0, 0, renderPass->width, renderPass->height, 0, 0,
renderPass->width, renderPass->height,
GL_COLOR_BUFFER_BIT, GL_NEAREST));
}
}
DAWN_GL_TRY(gl, Enable(GL_SCISSOR_TEST));
DAWN_GL_TRY(gl, DeleteFramebuffers(1, &readFbo));
DAWN_GL_TRY(gl, DeleteFramebuffers(1, &writeFbo));
return {};
}
// OpenGL SPEC requires the source/destination region muPst be a region that is contained
// within srcImage/dstImage. Here the size of the image refers to the virtual size, while
// Dawn validates texture copy extent with the physical size, so we need to re-calculate the
// texture copy extent to ensure it should fit in the virtual size of the subresource.
TexelExtent3D ComputeTextureCopyExtent(const TextureCopy& textureCopy,
const TexelExtent3D& copySize) {
TexelExtent3D validTextureCopyExtent = copySize;
const TextureBase* texture = textureCopy.texture.Get();
TexelExtent3D virtualSizeAtLevel =
texture->GetMipLevelSingleSubresourceVirtualSize(textureCopy.mipLevel, textureCopy.aspect);
DAWN_ASSERT(textureCopy.origin.x <= virtualSizeAtLevel.width);
DAWN_ASSERT(textureCopy.origin.y <= virtualSizeAtLevel.height);
if (copySize.width > virtualSizeAtLevel.width - textureCopy.origin.x) {
DAWN_ASSERT(texture->GetFormat().isCompressed);
validTextureCopyExtent.width = virtualSizeAtLevel.width - textureCopy.origin.x;
}
if (copySize.height > virtualSizeAtLevel.height - textureCopy.origin.y) {
DAWN_ASSERT(texture->GetFormat().isCompressed);
validTextureCopyExtent.height = virtualSizeAtLevel.height - textureCopy.origin.y;
}
return validTextureCopyExtent;
}
template <typename T>
class ImmediateConstantTracker : public T {
public:
ImmediateConstantTracker() = default;
MaybeError Apply(const OpenGLFunctions& gl) {
DAWN_ASSERT(this->mLastPipeline != nullptr);
auto* lastPipeline = this->mLastPipeline;
ImmediateConstantMask pipelineMask = lastPipeline->GetImmediateMask();
ImmediateConstantMask uploadBits = this->mDirty & pipelineMask;
for (auto&& [offset, size] : IterateRanges(uploadBits)) {
uint32_t immediateContentStartOffset =
static_cast<uint32_t>(offset) * kImmediateConstantElementByteSize;
auto location =
GetImmediateIndexInPipeline(static_cast<uint32_t>(offset), pipelineMask);
auto count = static_cast<uint32_t>(size);
auto value = this->mContent.template Get<uint32_t>(immediateContentStartOffset);
DAWN_GL_TRY(gl, Uniform1uiv(location, count, value));
}
// Reset all dirty bits after uploading.
this->mDirty.reset();
return {};
}
};
} // namespace
CommandBuffer::CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor)
: CommandBufferBase(encoder, descriptor) {}
MaybeError CommandBuffer::Execute(const OpenGLFunctions& gl) {
auto LazyClearSyncScope = [gl](const SyncScopeResourceUsage& scope) -> MaybeError {
for (size_t i = 0; i < scope.textures.size(); i++) {
Texture* texture = ToBackend(scope.textures[i]);
// Clear subresources that are not render attachments. Render attachments will be
// cleared in RecordBeginRenderPass by setting the loadop to clear when the texture
// subresource has not been initialized before the render pass.
DAWN_TRY(scope.textureSyncInfos[i].Iterate(
[&](const SubresourceRange& range, const TextureSyncInfo& syncInfo) -> MaybeError {
if (syncInfo.usage & ~wgpu::TextureUsage::RenderAttachment) {
DAWN_TRY(texture->EnsureSubresourceContentInitialized(gl, range));
}
return {};
}));
}
for (BufferBase* bufferBase : scope.buffers) {
DAWN_TRY(ToBackend(bufferBase)->EnsureDataInitialized());
}
return {};
};
size_t nextComputePassNumber = 0;
size_t nextRenderPassNumber = 0;
Command type;
while (mCommands.NextCommandId(&type)) {
switch (type) {
case Command::BeginComputePass: {
mCommands.NextCommand<BeginComputePassCmd>();
for (TextureBase* texture :
GetResourceUsages().computePasses[nextComputePassNumber].referencedTextures) {
DAWN_TRY(ToBackend(texture)->SynchronizeTextureBeforeUse());
}
for (const SyncScopeResourceUsage& scope :
GetResourceUsages().computePasses[nextComputePassNumber].dispatchUsages) {
DAWN_TRY(LazyClearSyncScope(scope));
}
DAWN_TRY(ExecuteComputePass(gl));
nextComputePassNumber++;
break;
}
case Command::BeginRenderPass: {
auto* cmd = mCommands.NextCommand<BeginRenderPassCmd>();
for (TextureBase* texture :
this->GetResourceUsages().renderPasses[nextRenderPassNumber].textures) {
DAWN_TRY(ToBackend(texture)->SynchronizeTextureBeforeUse());
}
DAWN_TRY(
LazyClearSyncScope(GetResourceUsages().renderPasses[nextRenderPassNumber]));
LazyClearRenderPassAttachments(GetDevice(), cmd);
DAWN_TRY(ExecuteRenderPass(cmd, gl));
nextRenderPassNumber++;
break;
}
case Command::CopyBufferToBuffer: {
CopyBufferToBufferCmd* copy = mCommands.NextCommand<CopyBufferToBufferCmd>();
if (copy->size == 0) {
// Skip no-op copies.
break;
}
DAWN_TRY(ToBackend(copy->source)->EnsureDataInitialized());
DAWN_TRY(
ToBackend(copy->destination)
->EnsureDataInitializedAsDestination(copy->destinationOffset, copy->size));
DAWN_GL_TRY(gl,
BindBuffer(GL_PIXEL_PACK_BUFFER, ToBackend(copy->source)->GetHandle()));
DAWN_GL_TRY(gl, BindBuffer(GL_PIXEL_UNPACK_BUFFER,
ToBackend(copy->destination)->GetHandle()));
DAWN_GL_TRY(
gl, CopyBufferSubData(GL_PIXEL_PACK_BUFFER, GL_PIXEL_UNPACK_BUFFER,
copy->sourceOffset, copy->destinationOffset, copy->size));
DAWN_GL_TRY(gl, BindBuffer(GL_PIXEL_PACK_BUFFER, 0));
DAWN_GL_TRY(gl, BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
ToBackend(copy->source)->TrackUsage();
ToBackend(copy->destination)->TrackUsage();
break;
}
case Command::CopyBufferToTexture: {
CopyBufferToTextureCmd* copy = mCommands.NextCommand<CopyBufferToTextureCmd>();
if (copy->copySize.IsEmpty()) {
// Skip no-op copies.
continue;
}
auto& src = copy->source;
auto& dst = copy->destination;
Buffer* buffer = ToBackend(src.buffer.Get());
Texture* texture = ToBackend(dst.texture.Get());
DAWN_TRY(texture->SynchronizeTextureBeforeUse());
DAWN_TRY(buffer->EnsureDataInitialized());
SubresourceRange range =
GetSubresourcesAffectedByCopy(dst, copy->copySize.ToExtent3D());
if (IsCompleteSubresourceCopiedTo(texture, copy->copySize.ToExtent3D(),
dst.mipLevel, dst.aspect)) {
texture->SetIsSubresourceContentInitialized(true, range);
} else {
DAWN_TRY(texture->EnsureSubresourceContentInitialized(gl, range));
}
DAWN_GL_TRY(gl, BindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->GetHandle()));
// TODO(crbug.com/424536624): Replace TexelCopyBufferLayout with BufferCopy
const TypedTexelBlockInfo& blockInfo = GetBlockInfo(dst);
TexelCopyBufferLayout dataLayout;
dataLayout.offset = 0;
dataLayout.bytesPerRow = blockInfo.ToBytes(src.blocksPerRow);
dataLayout.rowsPerImage = static_cast<uint32_t>(src.rowsPerImage);
DAWN_TRY(DoTexSubImage(gl, dst, reinterpret_cast<void*>(src.offset), dataLayout,
copy->copySize.ToExtent3D()));
DAWN_GL_TRY(gl, BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
buffer->TrackUsage();
break;
}
case Command::CopyTextureToBuffer: {
CopyTextureToBufferCmd* copy = mCommands.NextCommand<CopyTextureToBufferCmd>();
if (copy->copySize.IsEmpty()) {
// Skip no-op copies.
continue;
}
auto& src = copy->source;
auto& dst = copy->destination;
auto& copySize = copy->copySize;
Texture* texture = ToBackend(src.texture.Get());
Buffer* buffer = ToBackend(dst.buffer.Get());
const Format& formatInfo = texture->GetFormat();
const GLFormat& format = texture->GetGLFormat();
GLenum target = texture->GetGLTarget();
if (formatInfo.isCompressed) {
DAWN_UNREACHABLE();
}
DAWN_TRY(buffer->EnsureDataInitializedAsDestination(copy));
DAWN_TRY(texture->SynchronizeTextureBeforeUse());
SubresourceRange subresources =
GetSubresourcesAffectedByCopy(src, copy->copySize.ToExtent3D());
DAWN_TRY(texture->EnsureSubresourceContentInitialized(gl, subresources));
// The only way to move data from a texture to a buffer in GL is via
// glReadPixels with a pack buffer. Create a temporary FBO for the copy.
DAWN_GL_TRY(gl, BindTexture(target, texture->GetHandle()));
GLuint readFBO = 0;
DAWN_GL_TRY(gl, GenFramebuffers(1, &readFBO));
DAWN_GL_TRY(gl, BindFramebuffer(GL_READ_FRAMEBUFFER, readFBO));
const TypedTexelBlockInfo& blockInfo = GetBlockInfo(src);
DAWN_GL_TRY(gl, BindBuffer(GL_PIXEL_PACK_BUFFER, buffer->GetHandle()));
DAWN_GL_TRY(gl, PixelStorei(GL_PACK_ALIGNMENT, std::min(8u, blockInfo.byteSize)));
DAWN_GL_TRY(
gl, PixelStorei(GL_PACK_ROW_LENGTH, static_cast<uint32_t>(dst.blocksPerRow)));
GLenum glAttachment;
GLenum glFormat;
GLenum glType;
switch (src.aspect) {
case Aspect::Color:
glAttachment = GL_COLOR_ATTACHMENT0;
glFormat = format.format;
glType = format.type;
break;
case Aspect::Depth:
glAttachment = GL_DEPTH_ATTACHMENT;
glFormat = GL_DEPTH_COMPONENT;
glType = GL_FLOAT;
break;
case Aspect::Stencil:
glAttachment = GL_STENCIL_ATTACHMENT;
glFormat = GL_STENCIL_INDEX;
glType = GL_UNSIGNED_BYTE;
break;
case Aspect::CombinedDepthStencil:
case Aspect::None:
case Aspect::Plane0:
case Aspect::Plane1:
case Aspect::Plane2:
DAWN_UNREACHABLE();
}
uint8_t* offset = reinterpret_cast<uint8_t*>(static_cast<uintptr_t>(dst.offset));
switch (texture->GetDimension()) {
case wgpu::TextureDimension::Undefined:
DAWN_UNREACHABLE();
case wgpu::TextureDimension::e1D:
case wgpu::TextureDimension::e2D: {
if (target == GL_TEXTURE_2D) {
DAWN_ASSERT(texture->GetArrayLayers() == 1);
DAWN_GL_TRY(
gl, FramebufferTexture2D(GL_READ_FRAMEBUFFER, glAttachment, target,
texture->GetHandle(), src.mipLevel));
DAWN_GL_TRY(gl, ReadPixels(static_cast<uint32_t>(src.origin.x),
static_cast<uint32_t>(src.origin.y),
static_cast<uint32_t>(copySize.width),
static_cast<uint32_t>(copySize.height),
glFormat, glType, offset));
break;
} else if (target == GL_TEXTURE_CUBE_MAP) {
DAWN_ASSERT(texture->GetArrayLayers() == 6);
const uint64_t bytesPerImage =
blockInfo.ToBytes(dst.blocksPerRow * dst.rowsPerImage);
for (TexelCount z{0}; z < copySize.depthOrArrayLayers; ++z) {
GLenum cubeMapTarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X +
static_cast<uint32_t>(z + src.origin.z);
DAWN_GL_TRY(
gl, FramebufferTexture2D(GL_READ_FRAMEBUFFER, glAttachment,
cubeMapTarget, texture->GetHandle(),
src.mipLevel));
DAWN_GL_TRY(gl, ReadPixels(static_cast<uint32_t>(src.origin.x),
static_cast<uint32_t>(src.origin.y),
static_cast<uint32_t>(copySize.width),
static_cast<uint32_t>(copySize.height),
glFormat, glType, offset));
offset += bytesPerImage;
}
break;
}
// Implementation for 2D array is the same as 3D.
[[fallthrough]];
}
case wgpu::TextureDimension::e3D: {
const uint64_t bytesPerImage =
blockInfo.ToBytes(dst.blocksPerRow * dst.rowsPerImage);
for (TexelCount z{0}; z < copySize.depthOrArrayLayers; ++z) {
DAWN_GL_TRY(gl,
FramebufferTextureLayer(
GL_READ_FRAMEBUFFER, glAttachment, texture->GetHandle(),
src.mipLevel, static_cast<uint32_t>(src.origin.z + z)));
DAWN_GL_TRY(gl, ReadPixels(static_cast<uint32_t>(src.origin.x),
static_cast<uint32_t>(src.origin.y),
static_cast<uint32_t>(copySize.width),
static_cast<uint32_t>(copySize.height),
glFormat, glType, offset));
offset += bytesPerImage;
}
break;
}
}
DAWN_GL_TRY(gl, PixelStorei(GL_PACK_ROW_LENGTH, 0));
DAWN_GL_TRY(gl, PixelStorei(GL_PACK_ALIGNMENT, 4)); // Reset to default
DAWN_GL_TRY(gl, BindBuffer(GL_PIXEL_PACK_BUFFER, 0));
DAWN_GL_TRY(gl, DeleteFramebuffers(1, &readFBO));
buffer->TrackUsage();
break;
}
case Command::CopyTextureToTexture: {
CopyTextureToTextureCmd* copy = mCommands.NextCommand<CopyTextureToTextureCmd>();
if (copy->copySize.IsEmpty()) {
// Skip no-op copies.
continue;
}
auto& src = copy->source;
auto& dst = copy->destination;
// TODO(crbug.com/dawn/817): add workaround for the case that imageExtentSrc
// is not equal to imageExtentDst. For example when copySize fits in the virtual
// size of the source image but does not fit in the one of the destination
// image.
TexelExtent3D copySize = ComputeTextureCopyExtent(dst, copy->copySize);
Texture* srcTexture = ToBackend(src.texture.Get());
Texture* dstTexture = ToBackend(dst.texture.Get());
DAWN_TRY(srcTexture->SynchronizeTextureBeforeUse());
DAWN_TRY(dstTexture->SynchronizeTextureBeforeUse());
SubresourceRange srcRange =
GetSubresourcesAffectedByCopy(src, copy->copySize.ToExtent3D());
SubresourceRange dstRange =
GetSubresourcesAffectedByCopy(dst, copy->copySize.ToExtent3D());
DAWN_TRY(srcTexture->EnsureSubresourceContentInitialized(gl, srcRange));
if (IsCompleteSubresourceCopiedTo(dstTexture, copySize.ToExtent3D(), dst.mipLevel,
dst.aspect)) {
dstTexture->SetIsSubresourceContentInitialized(true, dstRange);
} else {
DAWN_TRY(dstTexture->EnsureSubresourceContentInitialized(gl, dstRange));
}
DAWN_TRY(CopyImageSubData(gl, src.aspect, srcTexture->GetHandle(),
srcTexture->GetGLTarget(), src.mipLevel,
src.origin.ToOrigin3D(), dstTexture->GetHandle(),
dstTexture->GetGLTarget(), dst.mipLevel,
dst.origin.ToOrigin3D(), copySize.ToExtent3D()));
break;
}
case Command::ClearBuffer: {
ClearBufferCmd* cmd = mCommands.NextCommand<ClearBufferCmd>();
if (cmd->size == 0) {
// Skip no-op fills.
break;
}
Buffer* dstBuffer = ToBackend(cmd->buffer.Get());
bool clearedToZero = false;
DAWN_TRY(dstBuffer->EnsureDataInitializedAsDestination(cmd->offset, cmd->size,
&clearedToZero));
if (!clearedToZero) {
const std::vector<uint8_t> clearValues(cmd->size, 0u);
DAWN_GL_TRY(gl, BindBuffer(GL_ARRAY_BUFFER, dstBuffer->GetHandle()));
DAWN_GL_TRY(gl, BufferSubData(GL_ARRAY_BUFFER, cmd->offset, cmd->size,
clearValues.data()));
}
dstBuffer->TrackUsage();
break;
}
case Command::ResolveQuerySet: {
ResolveQuerySetCmd* cmd = mCommands.NextCommand<ResolveQuerySetCmd>();
QuerySet* querySet = ToBackend(cmd->querySet.Get());
Buffer* destination = ToBackend(cmd->destination.Get());
size_t size = cmd->queryCount * sizeof(uint64_t);
DAWN_TRY(
destination->EnsureDataInitializedAsDestination(cmd->destinationOffset, size));
std::vector<uint64_t> values(cmd->queryCount);
auto availability = querySet->GetQueryAvailability();
for (uint32_t i = 0; i < cmd->queryCount; ++i) {
if (!availability[cmd->firstQuery + i]) {
values[i] = 0;
continue;
}
uint32_t query = querySet->Get(cmd->firstQuery + i);
GLuint value;
DAWN_GL_TRY(gl, GetQueryObjectuiv(query, GL_QUERY_RESULT, &value));
values[i] = value;
}
DAWN_GL_TRY(gl, BindBuffer(GL_ARRAY_BUFFER, destination->GetHandle()));
DAWN_GL_TRY(gl, BufferSubData(GL_ARRAY_BUFFER, cmd->destinationOffset, size,
values.data()));
break;
}
case Command::WriteTimestamp: {
return DAWN_UNIMPLEMENTED_ERROR("WriteTimestamp unimplemented");
}
case Command::InsertDebugMarker:
case Command::PopDebugGroup:
case Command::PushDebugGroup: {
// Due to lack of linux driver support for GL_EXT_debug_marker
// extension these functions are skipped.
SkipCommand(&mCommands, type);
break;
}
case Command::WriteBuffer: {
WriteBufferCmd* write = mCommands.NextCommand<WriteBufferCmd>();
uint64_t offset = write->offset;
uint64_t size = write->size;
if (size == 0) {
continue;
}
Buffer* dstBuffer = ToBackend(write->buffer.Get());
uint8_t* data = mCommands.NextData<uint8_t>(size);
DAWN_TRY(dstBuffer->EnsureDataInitializedAsDestination(offset, size));
DAWN_GL_TRY(gl, BindBuffer(GL_ARRAY_BUFFER, dstBuffer->GetHandle()));
DAWN_GL_TRY(gl, BufferSubData(GL_ARRAY_BUFFER, offset, size, data));
dstBuffer->TrackUsage();
break;
}
default:
DAWN_UNREACHABLE();
}
}
return {};
}
MaybeError CommandBuffer::ExecuteComputePass(const OpenGLFunctions& gl) {
ComputePipeline* lastPipeline = nullptr;
BindGroupTracker bindGroupTracker = {};
Command type;
ImmediateConstantTracker<ComputeImmediateConstantsTrackerBase> immediates = {};
while (mCommands.NextCommandId(&type)) {
switch (type) {
case Command::EndComputePass: {
mCommands.NextCommand<EndComputePassCmd>();
return {};
}
case Command::Dispatch: {
DispatchCmd* dispatch = mCommands.NextCommand<DispatchCmd>();
DAWN_TRY(bindGroupTracker.Apply(gl));
DAWN_TRY(immediates.Apply(gl));
DAWN_GL_TRY(gl, DispatchCompute(dispatch->x, dispatch->y, dispatch->z));
DAWN_GL_TRY(gl, MemoryBarrier(GL_ALL_BARRIER_BITS));
break;
}
case Command::DispatchIndirect: {
DispatchIndirectCmd* dispatch = mCommands.NextCommand<DispatchIndirectCmd>();
DAWN_TRY(bindGroupTracker.Apply(gl));
DAWN_TRY(immediates.Apply(gl));
uint64_t indirectBufferOffset = dispatch->indirectOffset;
Buffer* indirectBuffer = ToBackend(dispatch->indirectBuffer.Get());
DAWN_GL_TRY(gl,
BindBuffer(GL_DISPATCH_INDIRECT_BUFFER, indirectBuffer->GetHandle()));
DAWN_GL_TRY(gl,
DispatchComputeIndirect(static_cast<GLintptr>(indirectBufferOffset)));
DAWN_GL_TRY(gl, MemoryBarrier(GL_ALL_BARRIER_BITS));
indirectBuffer->TrackUsage();
break;
}
case Command::SetComputePipeline: {
SetComputePipelineCmd* cmd = mCommands.NextCommand<SetComputePipelineCmd>();
lastPipeline = ToBackend(cmd->pipeline).Get();
DAWN_TRY(lastPipeline->ApplyNow(gl));
bindGroupTracker.OnSetPipeline(lastPipeline);
immediates.OnSetPipeline(lastPipeline);
break;
}
case Command::SetBindGroup: {
SetBindGroupCmd* cmd = mCommands.NextCommand<SetBindGroupCmd>();
uint32_t* dynamicOffsets = nullptr;
if (cmd->dynamicOffsetCount > 0) {
dynamicOffsets = mCommands.NextData<uint32_t>(cmd->dynamicOffsetCount);
}
bindGroupTracker.OnSetBindGroup(cmd->index, cmd->group.Get(),
cmd->dynamicOffsetCount, dynamicOffsets);
break;
}
case Command::InsertDebugMarker:
case Command::PopDebugGroup:
case Command::PushDebugGroup: {
// Due to lack of linux driver support for GL_EXT_debug_marker
// extension these functions are skipped.
SkipCommand(&mCommands, type);
break;
}
case Command::WriteTimestamp: {
return DAWN_UNIMPLEMENTED_ERROR("WriteTimestamp unimplemented");
}
case Command::SetImmediates: {
SetImmediatesCmd* cmd = mCommands.NextCommand<SetImmediatesCmd>();
uint8_t* values = mCommands.NextData<uint8_t>(cmd->size);
immediates.SetImmediates(cmd->offset, values, cmd->size);
break;
}
default:
DAWN_UNREACHABLE();
}
}
// EndComputePass should have been called
DAWN_UNREACHABLE();
}
MaybeError CommandBuffer::ExecuteRenderPass(BeginRenderPassCmd* renderPass,
const OpenGLFunctions& gl) {
GLuint fbo = 0;
// Create the framebuffer used for this render pass and calls the correct glDrawBuffers
{
// TODO(kainino@chromium.org): This is added to possibly work around an issue seen on
// Windows/Intel. It should break any feedback loop before the clears, even if there
// shouldn't be any negative effects from this. Investigate whether it's actually
// needed.
DAWN_GL_TRY(gl, BindFramebuffer(GL_READ_FRAMEBUFFER, 0));
// TODO(kainino@chromium.org): possible future optimization: create these framebuffers
// at Framebuffer build time (or maybe CommandBuffer build time) so they don't have to
// be created and destroyed at draw time.
DAWN_GL_TRY(gl, GenFramebuffers(1, &fbo));
DAWN_GL_TRY(gl, BindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo));
// Mapping from attachmentSlot to GL framebuffer attachment points. Defaults to zero
// (GL_NONE).
PerColorAttachment<GLenum> drawBuffers = {};
// Construct GL framebuffer
ColorAttachmentIndex attachmentCount{};
for (auto i : renderPass->attachmentState->GetColorAttachmentsMask()) {
TextureView* textureView = ToBackend(renderPass->colorAttachments[i].view.Get());
GLenum glAttachment = GL_COLOR_ATTACHMENT0 + static_cast<uint8_t>(i);
// Attach color buffers.
DAWN_TRY(textureView->BindToFramebuffer(gl, GL_DRAW_FRAMEBUFFER, glAttachment,
renderPass->colorAttachments[i].depthSlice));
drawBuffers[i] = glAttachment;
attachmentCount = ityp::PlusOne(i);
}
DAWN_GL_TRY(gl, DrawBuffers(static_cast<uint8_t>(attachmentCount), drawBuffers.data()));
if (renderPass->attachmentState->HasDepthStencilAttachment()) {
TextureView* textureView = ToBackend(renderPass->depthStencilAttachment.view.Get());
const Format& format = textureView->GetTexture()->GetFormat();
// Attach depth/stencil buffer.
GLenum glAttachment = 0;
if (format.aspects == (Aspect::Depth | Aspect::Stencil)) {
glAttachment = GL_DEPTH_STENCIL_ATTACHMENT;
} else if (format.aspects == Aspect::Depth) {
glAttachment = GL_DEPTH_ATTACHMENT;
} else if (format.aspects == Aspect::Stencil) {
glAttachment = GL_STENCIL_ATTACHMENT;
} else {
DAWN_UNREACHABLE();
}
DAWN_TRY(textureView->BindToFramebuffer(gl, GL_DRAW_FRAMEBUFFER, glAttachment));
}
}
DAWN_ASSERT(gl.CheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
// Set defaults for dynamic state before executing clears and commands.
PersistentPipelineState persistentPipelineState;
DAWN_TRY(persistentPipelineState.SetDefaultState(gl));
DAWN_GL_TRY(gl, BlendColor(0, 0, 0, 0));
DAWN_GL_TRY(gl, Viewport(0, 0, renderPass->width, renderPass->height));
float minDepth = 0.0f;
float maxDepth = 1.0f;
DAWN_GL_TRY(gl, DepthRangef(minDepth, maxDepth));
DAWN_GL_TRY(gl, Scissor(0, 0, renderPass->width, renderPass->height));
// Clear framebuffer attachments as needed
{
for (auto index : renderPass->attachmentState->GetColorAttachmentsMask()) {
uint8_t i = static_cast<uint8_t>(index);
auto* attachmentInfo = &renderPass->colorAttachments[index];
// Load op - color
if (attachmentInfo->loadOp == wgpu::LoadOp::Clear) {
DAWN_GL_TRY(gl, ColorMask(true, true, true, true));
TextureComponentType baseType =
attachmentInfo->view->GetFormat().GetAspectInfo(Aspect::Color).baseType;
switch (baseType) {
case TextureComponentType::Float: {
const std::array<float, 4> appliedClearColor =
ConvertToFloatColor(attachmentInfo->clearColor);
DAWN_GL_TRY(gl, ClearBufferfv(GL_COLOR, i, appliedClearColor.data()));
break;
}
case TextureComponentType::Uint: {
const std::array<uint32_t, 4> appliedClearColor =
ConvertToUnsignedIntegerColor(attachmentInfo->clearColor);
DAWN_GL_TRY(gl, ClearBufferuiv(GL_COLOR, i, appliedClearColor.data()));
break;
}
case TextureComponentType::Sint: {
const std::array<int32_t, 4> appliedClearColor =
ConvertToSignedIntegerColor(attachmentInfo->clearColor);
DAWN_GL_TRY(gl, ClearBufferiv(GL_COLOR, i, appliedClearColor.data()));
break;
}
}
}
if (attachmentInfo->storeOp == wgpu::StoreOp::Discard) {
// TODO(natlee@microsoft.com): call glDiscard to do optimization
}
}
if (renderPass->attachmentState->HasDepthStencilAttachment()) {
auto* attachmentInfo = &renderPass->depthStencilAttachment;
const Format& attachmentFormat = attachmentInfo->view->GetTexture()->GetFormat();
// Load op - depth/stencil
bool doDepthClear =
attachmentFormat.HasDepth() && (attachmentInfo->depthLoadOp == wgpu::LoadOp::Clear);
bool doStencilClear = attachmentFormat.HasStencil() &&
(attachmentInfo->stencilLoadOp == wgpu::LoadOp::Clear);
if (doDepthClear) {
DAWN_GL_TRY(gl, DepthMask(GL_TRUE));
}
if (doStencilClear) {
DAWN_GL_TRY(gl,
StencilMask(GetStencilMaskFromStencilFormat(attachmentFormat.format)));
}
if (doDepthClear && doStencilClear) {
DAWN_GL_TRY(gl, ClearBufferfi(GL_DEPTH_STENCIL, 0, attachmentInfo->clearDepth,
attachmentInfo->clearStencil));
} else if (doDepthClear) {
DAWN_GL_TRY(gl, ClearBufferfv(GL_DEPTH, 0, &attachmentInfo->clearDepth));
} else if (doStencilClear) {
const GLint clearStencil = attachmentInfo->clearStencil;
DAWN_GL_TRY(gl, ClearBufferiv(GL_STENCIL, 0, &clearStencil));
}
}
}
RenderPipeline* lastPipeline = nullptr;
uint64_t indexBufferBaseOffset = 0;
GLenum indexBufferFormat;
uint32_t indexFormatSize;
VertexStateBufferBindingTracker vertexStateBufferBindingTracker;
BindGroupTracker bindGroupTracker = {};
ImmediateConstantTracker<RenderImmediateConstantsTrackerBase> immediates = {};
auto DoRenderBundleCommand = [&](CommandIterator* iter, Command type) -> MaybeError {
switch (type) {
case Command::Draw: {
DrawCmd* draw = iter->NextCommand<DrawCmd>();
DAWN_TRY(vertexStateBufferBindingTracker.Apply(gl, 0, draw->firstInstance));
DAWN_TRY(bindGroupTracker.Apply(gl));
immediates.SetFirstVertex(0);
immediates.SetFirstInstance(draw->firstInstance);
DAWN_TRY(immediates.Apply(gl));
DAWN_GL_TRY(gl, DrawArraysInstanced(lastPipeline->GetGLPrimitiveTopology(),
draw->firstVertex, draw->vertexCount,
draw->instanceCount));
break;
}
case Command::DrawIndexed: {
DrawIndexedCmd* draw = iter->NextCommand<DrawIndexedCmd>();
DAWN_TRY(vertexStateBufferBindingTracker.Apply(gl, draw->baseVertex,
draw->firstInstance));
DAWN_TRY(bindGroupTracker.Apply(gl));
const auto topology = lastPipeline->GetGLPrimitiveTopology();
if (topology == GL_LINE_STRIP || topology == GL_TRIANGLE_STRIP) {
DAWN_GL_TRY(gl, Enable(GL_PRIMITIVE_RESTART_FIXED_INDEX));
} else {
DAWN_GL_TRY(gl, Disable(GL_PRIMITIVE_RESTART_FIXED_INDEX));
}
immediates.SetFirstVertex(draw->baseVertex);
immediates.SetFirstInstance(draw->firstInstance);
DAWN_TRY(immediates.Apply(gl));
DAWN_GL_TRY(gl, DrawElementsInstanced(
topology, draw->indexCount, indexBufferFormat,
reinterpret_cast<void*>(draw->firstIndex * indexFormatSize +
indexBufferBaseOffset),
draw->instanceCount));
break;
}
case Command::DrawIndirect: {
DrawIndirectCmd* draw = iter->NextCommand<DrawIndirectCmd>();
immediates.SetFirstInstance(0);
DAWN_TRY(immediates.Apply(gl));
DAWN_TRY(vertexStateBufferBindingTracker.Apply(gl, 0, 0));
DAWN_TRY(bindGroupTracker.Apply(gl));
uint64_t indirectBufferOffset = draw->indirectOffset;
Buffer* indirectBuffer = ToBackend(draw->indirectBuffer.Get());
DAWN_GL_TRY(gl, BindBuffer(GL_DRAW_INDIRECT_BUFFER, indirectBuffer->GetHandle()));
DAWN_GL_TRY(
gl, DrawArraysIndirect(
lastPipeline->GetGLPrimitiveTopology(),
reinterpret_cast<void*>(static_cast<intptr_t>(indirectBufferOffset))));
indirectBuffer->TrackUsage();
break;
}
case Command::DrawIndexedIndirect: {
DrawIndexedIndirectCmd* draw = iter->NextCommand<DrawIndexedIndirectCmd>();
immediates.SetFirstInstance(0);
DAWN_TRY(immediates.Apply(gl));
DAWN_TRY(vertexStateBufferBindingTracker.Apply(gl, 0, 0));
DAWN_TRY(bindGroupTracker.Apply(gl));
Buffer* indirectBuffer = ToBackend(draw->indirectBuffer.Get());
DAWN_ASSERT(indirectBuffer != nullptr);
const auto topology = lastPipeline->GetGLPrimitiveTopology();
if (topology == GL_LINE_STRIP || topology == GL_TRIANGLE_STRIP) {
DAWN_GL_TRY(gl, Enable(GL_PRIMITIVE_RESTART_FIXED_INDEX));
} else {
DAWN_GL_TRY(gl, Disable(GL_PRIMITIVE_RESTART_FIXED_INDEX));
}
DAWN_GL_TRY(gl, BindBuffer(GL_DRAW_INDIRECT_BUFFER, indirectBuffer->GetHandle()));
DAWN_GL_TRY(
gl, DrawElementsIndirect(
topology, indexBufferFormat,
reinterpret_cast<void*>(static_cast<intptr_t>(draw->indirectOffset))));
indirectBuffer->TrackUsage();
break;
}
case Command::InsertDebugMarker:
case Command::PopDebugGroup:
case Command::PushDebugGroup: {
// Due to lack of linux driver support for GL_EXT_debug_marker
// extension these functions are skipped.
SkipCommand(iter, type);
break;
}
case Command::SetRenderPipeline: {
SetRenderPipelineCmd* cmd = iter->NextCommand<SetRenderPipelineCmd>();
lastPipeline = ToBackend(cmd->pipeline).Get();
DAWN_TRY(lastPipeline->ApplyNow(gl, persistentPipelineState));
vertexStateBufferBindingTracker.OnSetPipeline(lastPipeline);
bindGroupTracker.OnSetPipeline(lastPipeline);
immediates.OnSetPipeline(lastPipeline);
immediates.SetClampFragDepth(minDepth, maxDepth);
break;
}
case Command::SetBindGroup: {
SetBindGroupCmd* cmd = iter->NextCommand<SetBindGroupCmd>();
uint32_t* dynamicOffsets = nullptr;
if (cmd->dynamicOffsetCount > 0) {
dynamicOffsets = iter->NextData<uint32_t>(cmd->dynamicOffsetCount);
}
bindGroupTracker.OnSetBindGroup(cmd->index, cmd->group.Get(),
cmd->dynamicOffsetCount, dynamicOffsets);
break;
}
case Command::SetIndexBuffer: {
SetIndexBufferCmd* cmd = iter->NextCommand<SetIndexBufferCmd>();
indexBufferBaseOffset = cmd->offset;
indexBufferFormat = IndexFormatType(cmd->format);
indexFormatSize = IndexFormatSize(cmd->format);
vertexStateBufferBindingTracker.OnSetIndexBuffer(cmd->buffer.Get());
ToBackend(cmd->buffer)->TrackUsage();
break;
}
case Command::SetVertexBuffer: {
SetVertexBufferCmd* cmd = iter->NextCommand<SetVertexBufferCmd>();
vertexStateBufferBindingTracker.OnSetVertexBuffer(cmd->slot, cmd->buffer.Get(),
cmd->offset);
ToBackend(cmd->buffer)->TrackUsage();
break;
}
case Command::SetImmediates: {
SetImmediatesCmd* cmd = iter->NextCommand<SetImmediatesCmd>();
uint8_t* values = iter->NextData<uint8_t>(cmd->size);
immediates.SetImmediates(cmd->offset, values, cmd->size);
break;
}
default:
DAWN_UNREACHABLE();
break;
}
return {};
};
Command type;
while (mCommands.NextCommandId(&type)) {
switch (type) {
case Command::EndRenderPass: {
mCommands.NextCommand<EndRenderPassCmd>();
if (renderPass->attachmentState->GetSampleCount() > 1) {
DAWN_TRY(ResolveMultisampledRenderTargets(gl, renderPass));
}
DAWN_GL_TRY(gl, DeleteFramebuffers(1, &fbo));
return {};
}
case Command::SetStencilReference: {
SetStencilReferenceCmd* cmd = mCommands.NextCommand<SetStencilReferenceCmd>();
DAWN_TRY(persistentPipelineState.SetStencilReference(gl, cmd->reference));
break;
}
case Command::SetViewport: {
SetViewportCmd* cmd = mCommands.NextCommand<SetViewportCmd>();
if (gl.IsAtLeastGL(4, 1)) {
DAWN_GL_TRY(gl, ViewportIndexedf(0, cmd->x, cmd->y, cmd->width, cmd->height));
} else {
// Floating-point viewport coords are unsupported on OpenGL ES, but
// truncation is ok because other APIs do not guarantee subpixel precision
// either.
DAWN_GL_TRY(
gl, Viewport(static_cast<int>(cmd->x), static_cast<int>(cmd->y),
static_cast<int>(cmd->width), static_cast<int>(cmd->height)));
}
minDepth = cmd->minDepth;
maxDepth = cmd->maxDepth;
DAWN_GL_TRY(gl, DepthRangef(minDepth, maxDepth));
immediates.SetClampFragDepth(minDepth, maxDepth);
break;
}
case Command::SetScissorRect: {
SetScissorRectCmd* cmd = mCommands.NextCommand<SetScissorRectCmd>();
DAWN_GL_TRY(gl, Scissor(cmd->x, cmd->y, cmd->width, cmd->height));
break;
}
case Command::SetBlendConstant: {
SetBlendConstantCmd* cmd = mCommands.NextCommand<SetBlendConstantCmd>();
const std::array<float, 4> blendColor = ConvertToFloatColor(cmd->color);
DAWN_GL_TRY(gl,
BlendColor(blendColor[0], blendColor[1], blendColor[2], blendColor[3]));
break;
}
case Command::ExecuteBundles: {
ExecuteBundlesCmd* cmd = mCommands.NextCommand<ExecuteBundlesCmd>();
auto bundles = mCommands.NextData<Ref<RenderBundleBase>>(cmd->count);
for (uint32_t i = 0; i < cmd->count; ++i) {
CommandIterator* iter = bundles[i]->GetCommands();
iter->Reset();
while (iter->NextCommandId(&type)) {
DAWN_TRY(DoRenderBundleCommand(iter, type));
}
}
break;
}
case Command::BeginOcclusionQuery: {
BeginOcclusionQueryCmd* cmd = mCommands.NextCommand<BeginOcclusionQueryCmd>();
QuerySet* querySet = ToBackend(renderPass->occlusionQuerySet.Get());
DAWN_GL_TRY(gl, BeginQuery(GL_ANY_SAMPLES_PASSED, querySet->Get(cmd->queryIndex)));
break;
}
case Command::EndOcclusionQuery: {
mCommands.NextCommand<EndOcclusionQueryCmd>();
DAWN_GL_TRY(gl, EndQuery(GL_ANY_SAMPLES_PASSED));
break;
}
case Command::WriteTimestamp:
return DAWN_UNIMPLEMENTED_ERROR("WriteTimestamp unimplemented");
default: {
DAWN_TRY(DoRenderBundleCommand(&mCommands, type));
break;
}
}
}
// EndRenderPass should have been called
DAWN_UNREACHABLE();
}
MaybeError DoTexSubImage(const OpenGLFunctions& gl,
const TextureCopy& destination,
const void* data,
const TexelCopyBufferLayout& dataLayout,
const TexelExtent3D& copySize) {
Texture* texture = ToBackend(destination.texture.Get());
const GLFormat& format = texture->GetGLFormat();
GLenum target = texture->GetGLTarget();
data = static_cast<const uint8_t*>(data) + dataLayout.offset;
DAWN_GL_TRY(gl, ActiveTexture(GL_TEXTURE0));
DAWN_GL_TRY(gl, BindTexture(target, texture->GetHandle()));
const TypedTexelBlockInfo& blockInfo = GetBlockInfo(destination);
const BlockExtent3D blockCopySize = blockInfo.ToBlock(copySize);
const uint64_t bytesPerImage = dataLayout.rowsPerImage * dataLayout.bytesPerRow;
const BlockCount rowsPerImage{dataLayout.rowsPerImage};
// Note: bytesPerRow is not necessarily a multiple of block size because WriteTexture is
// directly implemented by the GL backend and doesn't have alignment constraints for
// bytesPerRow.
const uint64_t bytesPerRow = dataLayout.bytesPerRow;
TexelCount x = destination.origin.x;
TexelCount y = destination.origin.y;
TexelCount z = destination.origin.z;
if (texture->GetFormat().isCompressed) {
size_t rowSize = blockInfo.ToBytes(blockCopySize.width);
TexelExtent3D virtSize = texture->GetMipLevelSingleSubresourceVirtualSize(
destination.mipLevel, destination.aspect);
const TexelCount width = std::min(copySize.width, virtSize.width - x);
// In GLES glPixelStorei() doesn't affect CompressedTexSubImage*D() and
// GL_UNPACK_COMPRESSED_BLOCK_* isn't defined, so we have to workaround
// this limitation by copying the compressed texture data once per row.
// See OpenGL ES 3.2 SPEC Chapter 8.4.1, "Pixel Storage Modes and Pixel
// Buffer Objects" for more details. For Desktop GL, we use row-by-row
// copies only for uploads where bytesPerRow is not a multiple of byteSize.
if (bytesPerRow % blockInfo.byteSize == 0 && gl.GetVersion().IsDesktop()) {
const BlockCount blocksPerRow = blockInfo.BytesToBlocks(bytesPerRow);
size_t imageSize = rowSize * blockInfo.ToBytes(blockCopySize.height *
blockCopySize.depthOrArrayLayers);
const TexelCount height = std::min(copySize.height, virtSize.height - y);
DAWN_GL_TRY(gl,
PixelStorei(GL_UNPACK_ROW_LENGTH,
static_cast<uint32_t>(blockInfo.ToTexelWidth(blocksPerRow))));
DAWN_GL_TRY(gl, PixelStorei(GL_UNPACK_COMPRESSED_BLOCK_SIZE, blockInfo.byteSize));
DAWN_GL_TRY(gl, PixelStorei(GL_UNPACK_COMPRESSED_BLOCK_WIDTH,
static_cast<uint32_t>(blockInfo.width)));
DAWN_GL_TRY(gl, PixelStorei(GL_UNPACK_COMPRESSED_BLOCK_HEIGHT,
static_cast<uint32_t>(blockInfo.height)));
DAWN_GL_TRY(gl, PixelStorei(GL_UNPACK_COMPRESSED_BLOCK_DEPTH, 1));
if (target == GL_TEXTURE_2D) {
DAWN_GL_TRY(
gl, CompressedTexSubImage2D(
target, destination.mipLevel, static_cast<uint32_t>(x),
static_cast<uint32_t>(y), static_cast<uint32_t>(width),
static_cast<uint32_t>(height), format.internalFormat, imageSize, data));
} else if (target == GL_TEXTURE_CUBE_MAP) {
DAWN_ASSERT(texture->GetArrayLayers() == 6);
const uint8_t* pointer = static_cast<const uint8_t*>(data);
TexelCount baseLayer = destination.origin.z;
for (TexelCount l{0}; l < copySize.depthOrArrayLayers; ++l) {
GLenum cubeMapTarget =
GL_TEXTURE_CUBE_MAP_POSITIVE_X + static_cast<uint32_t>(baseLayer + l);
DAWN_GL_TRY(gl, CompressedTexSubImage2D(
cubeMapTarget, destination.mipLevel,
static_cast<uint32_t>(x), static_cast<uint32_t>(y),
static_cast<uint32_t>(width), static_cast<uint32_t>(height),
format.internalFormat, imageSize, pointer));
pointer += bytesPerImage;
}
} else {
DAWN_GL_TRY(
gl, PixelStorei(GL_UNPACK_IMAGE_HEIGHT,
static_cast<uint32_t>(blockInfo.ToTexelHeight(rowsPerImage))));
DAWN_GL_TRY(gl, CompressedTexSubImage3D(
target, destination.mipLevel, static_cast<uint32_t>(x),
static_cast<uint32_t>(y), static_cast<uint32_t>(z),
static_cast<uint32_t>(width), static_cast<uint32_t>(height),
static_cast<uint32_t>(copySize.depthOrArrayLayers),
format.internalFormat, imageSize, data));
DAWN_GL_TRY(gl, PixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0));
}
DAWN_GL_TRY(gl, PixelStorei(GL_UNPACK_ROW_LENGTH, 0));
DAWN_GL_TRY(gl, PixelStorei(GL_UNPACK_COMPRESSED_BLOCK_SIZE, 0));
DAWN_GL_TRY(gl, PixelStorei(GL_UNPACK_COMPRESSED_BLOCK_WIDTH, 0));
DAWN_GL_TRY(gl, PixelStorei(GL_UNPACK_COMPRESSED_BLOCK_HEIGHT, 0));
DAWN_GL_TRY(gl, PixelStorei(GL_UNPACK_COMPRESSED_BLOCK_DEPTH, 0));
} else {
if (target == GL_TEXTURE_2D) {
const uint8_t* d = static_cast<const uint8_t*>(data);
for (; y < destination.origin.y + copySize.height; y += blockInfo.height) {
TexelCount height = std::min(blockInfo.height, virtSize.height - y);
DAWN_GL_TRY(
gl, CompressedTexSubImage2D(
target, destination.mipLevel, static_cast<uint32_t>(x),
static_cast<uint32_t>(y), static_cast<uint32_t>(width),
static_cast<uint32_t>(height), format.internalFormat, rowSize, d));
d += bytesPerRow;
}
} else if (target == GL_TEXTURE_CUBE_MAP) {
DAWN_ASSERT(texture->GetArrayLayers() == 6);
const uint8_t* pointer = static_cast<const uint8_t*>(data);
TexelCount baseLayer = destination.origin.z;
for (TexelCount l{0}; l < copySize.depthOrArrayLayers; ++l) {
const uint8_t* d = pointer + static_cast<uint32_t>(l) * bytesPerImage;
GLenum cubeMapTarget =
GL_TEXTURE_CUBE_MAP_POSITIVE_X + static_cast<uint32_t>(baseLayer + l);
for (y = destination.origin.y; y < destination.origin.y + copySize.height;
y += blockInfo.height) {
TexelCount height = std::min(blockInfo.height, virtSize.height - y);
DAWN_GL_TRY(gl, CompressedTexSubImage2D(cubeMapTarget, destination.mipLevel,
static_cast<uint32_t>(x),
static_cast<uint32_t>(y),
static_cast<uint32_t>(width),
static_cast<uint32_t>(height),
format.internalFormat, rowSize, d));
d += bytesPerRow;
}
}
} else {
DAWN_ASSERT(target == GL_TEXTURE_3D || target == GL_TEXTURE_2D_ARRAY ||
target == GL_TEXTURE_CUBE_MAP_ARRAY);
const uint8_t* slice = static_cast<const uint8_t*>(data);
for (; z < destination.origin.z + copySize.depthOrArrayLayers; ++z) {
const uint8_t* d = slice;
for (y = destination.origin.y; y < destination.origin.y + copySize.height;
y += blockInfo.height) {
TexelCount height = std::min(blockInfo.height, virtSize.height - y);
DAWN_GL_TRY(
gl, CompressedTexSubImage3D(
target, destination.mipLevel, static_cast<uint32_t>(x),
static_cast<uint32_t>(y), static_cast<uint32_t>(z),
static_cast<uint32_t>(width), static_cast<uint32_t>(height), 1,
format.internalFormat, rowSize, d));
d += bytesPerRow;
}
slice += bytesPerImage;
}
}
}
} else {
TexelCount width = copySize.width;
TexelCount height = copySize.height;
GLenum adjustedFormat = format.format;
if (format.format == GL_STENCIL) {
DAWN_ASSERT(gl.GetVersion().IsDesktop() ||
gl.IsGLExtensionSupported("GL_OES_texture_stencil8"));
adjustedFormat = GL_STENCIL_INDEX;
}
if (bytesPerRow % blockInfo.byteSize == 0) {
const BlockCount blocksPerRow = blockInfo.BytesToBlocks(bytesPerRow);
// Valid values for GL_UNPACK_ALIGNMENT are 1, 2, 4, 8
DAWN_GL_TRY(gl, PixelStorei(GL_UNPACK_ALIGNMENT, std::min(8u, blockInfo.byteSize)));
DAWN_GL_TRY(gl,
PixelStorei(GL_UNPACK_ROW_LENGTH,
static_cast<uint32_t>(blockInfo.ToTexelWidth(blocksPerRow))));
if (target == GL_TEXTURE_2D) {
DAWN_GL_TRY(
gl, TexSubImage2D(target, destination.mipLevel, static_cast<uint32_t>(x),
static_cast<uint32_t>(y), static_cast<uint32_t>(width),
static_cast<uint32_t>(height), adjustedFormat, format.type,
data));
} else if (target == GL_TEXTURE_CUBE_MAP) {
DAWN_ASSERT(texture->GetArrayLayers() == 6);
const uint8_t* pointer = static_cast<const uint8_t*>(data);
TexelCount baseLayer = destination.origin.z;
for (TexelCount l{0}; l < copySize.depthOrArrayLayers; ++l) {
GLenum cubeMapTarget =
GL_TEXTURE_CUBE_MAP_POSITIVE_X + static_cast<uint32_t>(baseLayer + l);
DAWN_GL_TRY(gl, TexSubImage2D(
cubeMapTarget, destination.mipLevel,
static_cast<uint32_t>(x), static_cast<uint32_t>(y),
static_cast<uint32_t>(width), static_cast<uint32_t>(height),
adjustedFormat, format.type, pointer));
pointer += bytesPerImage;
}
} else {
DAWN_ASSERT(target == GL_TEXTURE_3D || target == GL_TEXTURE_2D_ARRAY ||
target == GL_TEXTURE_CUBE_MAP_ARRAY);
DAWN_GL_TRY(
gl, PixelStorei(GL_UNPACK_IMAGE_HEIGHT,
static_cast<uint32_t>(blockInfo.ToTexelHeight(rowsPerImage))));
DAWN_GL_TRY(
gl, TexSubImage3D(target, destination.mipLevel, static_cast<uint32_t>(x),
static_cast<uint32_t>(y), static_cast<uint32_t>(z),
static_cast<uint32_t>(width), static_cast<uint32_t>(height),
static_cast<uint32_t>(copySize.depthOrArrayLayers),
adjustedFormat, format.type, data));
DAWN_GL_TRY(gl, PixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0));
}
DAWN_GL_TRY(gl, PixelStorei(GL_UNPACK_ROW_LENGTH, 0));
DAWN_GL_TRY(gl, PixelStorei(GL_UNPACK_ALIGNMENT, 4)); // Reset to default
} else {
if (target == GL_TEXTURE_2D) {
const uint8_t* d = static_cast<const uint8_t*>(data);
for (; y < destination.origin.y + height; ++y) {
DAWN_GL_TRY(
gl, TexSubImage2D(target, destination.mipLevel, static_cast<uint32_t>(x),
static_cast<uint32_t>(y), static_cast<uint32_t>(width), 1,
adjustedFormat, format.type, d));
d += bytesPerRow;
}
} else if (target == GL_TEXTURE_CUBE_MAP) {
DAWN_ASSERT(texture->GetArrayLayers() == 6);
const uint8_t* pointer = static_cast<const uint8_t*>(data);
TexelCount baseLayer = destination.origin.z;
for (TexelCount l{0}; l < copySize.depthOrArrayLayers; ++l) {
const uint8_t* d = pointer;
GLenum cubeMapTarget =
GL_TEXTURE_CUBE_MAP_POSITIVE_X + static_cast<uint32_t>(baseLayer + l);
for (y = destination.origin.y; y < destination.origin.y + height; ++y) {
DAWN_GL_TRY(
gl, TexSubImage2D(cubeMapTarget, destination.mipLevel,
static_cast<uint32_t>(x), static_cast<uint32_t>(y),
static_cast<uint32_t>(width), 1, adjustedFormat,
format.type, d));
d += bytesPerRow;
}
pointer += bytesPerImage;
}
} else {
DAWN_ASSERT(target == GL_TEXTURE_3D || target == GL_TEXTURE_2D_ARRAY ||
target == GL_TEXTURE_CUBE_MAP_ARRAY);
const uint8_t* slice = static_cast<const uint8_t*>(data);
for (; z < destination.origin.z + copySize.depthOrArrayLayers; ++z) {
const uint8_t* d = slice;
for (y = destination.origin.y; y < destination.origin.y + height; ++y) {
DAWN_GL_TRY(gl, TexSubImage3D(
target, destination.mipLevel, static_cast<uint32_t>(x),
static_cast<uint32_t>(y), static_cast<uint32_t>(z),
static_cast<uint32_t>(width), 1, 1, adjustedFormat,
format.type, d));
d += bytesPerRow;
}
slice += bytesPerImage;
}
}
}
}
return {};
}
} // namespace dawn::native::opengl