Skip to content

Commit

Permalink
Fix, correct implementation of setMinMaxLevels
Browse files Browse the repository at this point in the history
  • Loading branch information
bejado committed Sep 13, 2023
1 parent d9939bd commit ea639ce
Show file tree
Hide file tree
Showing 10 changed files with 361 additions and 164 deletions.
1 change: 1 addition & 0 deletions filament/backend/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ if (APPLE)
test/test_RenderExternalImage.cpp
test/test_StencilBuffer.cpp
test/test_Scissor.cpp
test/test_MipLevels.cpp
)

target_link_libraries(backend_test PRIVATE
Expand Down
8 changes: 4 additions & 4 deletions filament/backend/src/metal/MetalDriver.mm
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@
auto colorTexture = handle_cast<MetalTexture>(buffer.handle);
ASSERT_PRECONDITION(colorTexture->getMtlTextureForWrite(),
"Color texture passed to render target has no texture allocation");
colorTexture->updateLodRange(buffer.level);
colorTexture->extendLodRangeTo(buffer.level);
colorAttachments[i] = { colorTexture, color[i].level, color[i].layer };
}

Expand All @@ -356,7 +356,7 @@
auto depthTexture = handle_cast<MetalTexture>(depth.handle);
ASSERT_PRECONDITION(depthTexture->getMtlTextureForWrite(),
"Depth texture passed to render target has no texture allocation.");
depthTexture->updateLodRange(depth.level);
depthTexture->extendLodRangeTo(depth.level);
depthAttachment = { depthTexture, depth.level, depth.layer };
}

Expand All @@ -367,7 +367,7 @@
auto stencilTexture = handle_cast<MetalTexture>(stencil.handle);
ASSERT_PRECONDITION(stencilTexture->getMtlTextureForWrite(),
"Stencil texture passed to render target has no texture allocation.");
stencilTexture->updateLodRange(stencil.level);
stencilTexture->extendLodRangeTo(stencil.level);
stencilAttachment = { stencilTexture, stencil.level, stencil.layer };
}

Expand Down Expand Up @@ -790,7 +790,7 @@

void MetalDriver::setMinMaxLevels(Handle<HwTexture> th, uint32_t minLevel, uint32_t maxLevel) {
auto tex = handle_cast<MetalTexture>(th);
tex->updateLodRange(minLevel, maxLevel);
tex->setLodRange(minLevel, maxLevel);
}

void MetalDriver::update3DImage(Handle<HwTexture> th, uint32_t level,
Expand Down
4 changes: 2 additions & 2 deletions filament/backend/src/metal/MetalHandles.h
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,8 @@ class MetalTexture : public HwTexture {
// - using the texture as a render target attachment
// - calling setMinMaxLevels
// A texture's available mips are consistent throughout a render pass.
void updateLodRange(uint32_t level);
void updateLodRange(uint32_t minLevel, uint32_t maxLevel);
void setLodRange(uint32_t minLevel, uint32_t maxLevel);
void extendLodRangeTo(uint32_t level);

static MTLPixelFormat decidePixelFormat(MetalContext* context, TextureFormat format);

Expand Down
14 changes: 7 additions & 7 deletions filament/backend/src/metal/MetalHandles.mm
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ void presentDrawable(bool presentFrame, void* user) {
: HwTexture(target, levels, samples, width, height, depth, format, usage), context(context),
externalImage(context) {
texture = metalTexture;
updateLodRange(0, levels - 1);
setLodRange(0, levels - 1);
}

MetalTexture::~MetalTexture() {
Expand Down Expand Up @@ -658,14 +658,14 @@ void presentDrawable(bool presentFrame, void* user) {
}
}

updateLodRange(level);
extendLodRangeTo(level);
}

void MetalTexture::generateMipmaps() noexcept {
id <MTLBlitCommandEncoder> blitEncoder = [getPendingCommandBuffer(&context) blitCommandEncoder];
[blitEncoder generateMipmapsForTexture:texture];
[blitEncoder endEncoding];
updateLodRange(0, texture.mipmapLevelCount - 1);
setLodRange(0, texture.mipmapLevelCount - 1);
}

void MetalTexture::loadSlice(uint32_t level, MTLRegion region, uint32_t byteOffset, uint32_t slice,
Expand Down Expand Up @@ -788,18 +788,18 @@ void presentDrawable(bool presentFrame, void* user) {
context.blitter->blit(getPendingCommandBuffer(&context), args, "Texture upload blit");
}

void MetalTexture::updateLodRange(uint32_t level) {
void MetalTexture::extendLodRangeTo(uint32_t level) {
assert_invariant(!isInRenderPass(&context));
minLod = std::min(minLod, level);
maxLod = std::max(maxLod, level);
lodTextureView = nil;
}

void MetalTexture::updateLodRange(uint32_t min, uint32_t max) {
void MetalTexture::setLodRange(uint32_t min, uint32_t max) {
assert_invariant(!isInRenderPass(&context));
assert_invariant(min <= max);
minLod = std::min(minLod, min);
maxLod = std::max(maxLod, max);
minLod = min;
maxLod = max;
lodTextureView = nil;
}

Expand Down
61 changes: 0 additions & 61 deletions filament/backend/test/BackendTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,66 +211,5 @@ int runTests() {
return RUN_ALL_TESTS();
}

void getPixelInfo(PixelDataFormat format, PixelDataType type, size_t& outComponents, int& outBpp) {
assert_invariant(type != PixelDataType::COMPRESSED);
switch (format) {
case PixelDataFormat::UNUSED:
case PixelDataFormat::R:
case PixelDataFormat::R_INTEGER:
case PixelDataFormat::DEPTH_COMPONENT:
case PixelDataFormat::ALPHA:
outComponents = 1;
break;
case PixelDataFormat::RG:
case PixelDataFormat::RG_INTEGER:
case PixelDataFormat::DEPTH_STENCIL:
outComponents = 2;
break;
case PixelDataFormat::RGB:
case PixelDataFormat::RGB_INTEGER:
outComponents = 3;
break;
case PixelDataFormat::RGBA:
case PixelDataFormat::RGBA_INTEGER:
outComponents = 4;
break;
}

outBpp = outComponents;
switch (type) {
case PixelDataType::COMPRESSED: // Impossible -- to squash the IDE warnings
case PixelDataType::UBYTE:
case PixelDataType::BYTE:
// nothing to do
break;
case PixelDataType::USHORT:
case PixelDataType::SHORT:
case PixelDataType::HALF:
outBpp *= 2;
break;
case PixelDataType::UINT:
case PixelDataType::INT:
case PixelDataType::FLOAT:
outBpp *= 4;
break;
case PixelDataType::UINT_10F_11F_11F_REV:
// Special case, format must be RGB and uses 4 bytes
assert_invariant(format == PixelDataFormat::RGB);
outBpp = 4;
break;
case PixelDataType::UINT_2_10_10_10_REV:
// Special case, format must be RGBA and uses 4 bytes
assert_invariant(format == PixelDataFormat::RGBA);
outBpp = 4;
break;
case PixelDataType::USHORT_565:
// Special case, format must be RGB and uses 2 bytes
assert_invariant(format == PixelDataFormat::RGB);
outBpp = 2;
break;
}
}


} // namespace test

6 changes: 0 additions & 6 deletions filament/backend/test/BackendTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,6 @@ class BackendTest : public ::testing::Test {
filament::backend::Handle<filament::backend::HwBufferObject> uniform;
};


// Utilities

void getPixelInfo(filament::backend::PixelDataFormat format, filament::backend::PixelDataType type,
size_t& outComponents, int& outBpp);

} // namespace test

#endif
174 changes: 174 additions & 0 deletions filament/backend/test/BackendTestUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* 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.
*
*/

#ifndef TNT_BACKENDTESTUTILS_H
#define TNT_BACKENDTESTUTILS_H

#include <cstddef>

#include <backend/PixelBufferDescriptor.h>

using namespace filament;
using namespace filament::backend;

inline void getPixelInfo(PixelDataFormat format, PixelDataType type, size_t& outComponents, int& outBpp) {
assert_invariant(type != PixelDataType::COMPRESSED);
switch (format) {
case PixelDataFormat::UNUSED:
case PixelDataFormat::R:
case PixelDataFormat::R_INTEGER:
case PixelDataFormat::DEPTH_COMPONENT:
case PixelDataFormat::ALPHA:
outComponents = 1;
break;
case PixelDataFormat::RG:
case PixelDataFormat::RG_INTEGER:
case PixelDataFormat::DEPTH_STENCIL:
outComponents = 2;
break;
case PixelDataFormat::RGB:
case PixelDataFormat::RGB_INTEGER:
outComponents = 3;
break;
case PixelDataFormat::RGBA:
case PixelDataFormat::RGBA_INTEGER:
outComponents = 4;
break;
}

outBpp = outComponents;
switch (type) {
case PixelDataType::COMPRESSED: // Impossible -- to squash the IDE warnings
case PixelDataType::UBYTE:
case PixelDataType::BYTE:
// nothing to do
break;
case PixelDataType::USHORT:
case PixelDataType::SHORT:
case PixelDataType::HALF:
outBpp *= 2;
break;
case PixelDataType::UINT:
case PixelDataType::INT:
case PixelDataType::FLOAT:
outBpp *= 4;
break;
case PixelDataType::UINT_10F_11F_11F_REV:
// Special case, format must be RGB and uses 4 bytes
assert_invariant(format == PixelDataFormat::RGB);
outBpp = 4;
break;
case PixelDataType::UINT_2_10_10_10_REV:
// Special case, format must be RGBA and uses 4 bytes
assert_invariant(format == PixelDataFormat::RGBA);
outBpp = 4;
break;
case PixelDataType::USHORT_565:
// Special case, format must be RGB and uses 2 bytes
assert_invariant(format == PixelDataFormat::RGB);
outBpp = 2;
break;
}
}

template<typename ComponentType>
static void fillCheckerboard(void* buffer, size_t size, size_t stride, size_t components,
ComponentType value) {
ComponentType* row = (ComponentType*)buffer;
int p = 0;
for (int r = 0; r < size; r++) {
ComponentType* pixel = row;
for (int col = 0; col < size; col++) {
// Generate a checkerboard pattern.
if ((p & 0x0010) ^ ((p / size) & 0x0010)) {
// Turn on the first component (red).
pixel[0] = value;
}
pixel += components;
p++;
}
row += stride * components;
}
}

static PixelBufferDescriptor checkerboardPixelBuffer(PixelDataFormat format, PixelDataType type,
size_t size, size_t bufferPadding = 0) {
size_t components; int bpp;
getPixelInfo(format, type, components, bpp);

size_t bufferSize = size + bufferPadding * 2;
uint8_t* buffer = (uint8_t*) calloc(1, bufferSize * bufferSize * bpp);

uint8_t* ptr = buffer + (bufferSize * bufferPadding * bpp) + (bufferPadding * bpp);

switch (type) {
case PixelDataType::BYTE:
fillCheckerboard<int8_t>(ptr, size, bufferSize, components, 1);
break;

case PixelDataType::UBYTE:
fillCheckerboard<uint8_t>(ptr, size, bufferSize, components, 0xFF);
break;

case PixelDataType::SHORT:
fillCheckerboard<int16_t>(ptr, size, bufferSize, components, 1);
break;

case PixelDataType::USHORT:
fillCheckerboard<uint16_t>(ptr, size, bufferSize, components, 1u);
break;

case PixelDataType::UINT:
fillCheckerboard<uint32_t>(ptr, size, bufferSize, components, 1u);
break;

case PixelDataType::INT:
fillCheckerboard<int32_t>(ptr, size, bufferSize, components, 1);
break;

case PixelDataType::FLOAT:
fillCheckerboard<float>(ptr, size, bufferSize, components, 1.0f);
break;

case PixelDataType::HALF:
fillCheckerboard<math::half>(ptr, size, bufferSize, components, math::half(1.0f));
break;

case PixelDataType::UINT_2_10_10_10_REV:
fillCheckerboard<uint32_t>(ptr, size, bufferSize, 1, 0xC00003FF /* red */);
break;

case PixelDataType::USHORT_565:
fillCheckerboard<uint16_t>(ptr, size, bufferSize, 1, 0xF800 /* red */);
break;

case PixelDataType::UINT_10F_11F_11F_REV:
fillCheckerboard<uint32_t>(ptr, size, bufferSize, 1, 0x000003C0 /* red */);
break;

case PixelDataType::COMPRESSED:
break;
}

PixelBufferDescriptor descriptor(buffer, bufferSize * bufferSize * bpp, format, type,
1, bufferPadding, bufferPadding, bufferSize, [](void* buffer, size_t size, void* user) {
free(buffer);
}, nullptr);
return descriptor;
}

#endif // TNT_BACKENDTESTUTILS_H
Loading

0 comments on commit ea639ce

Please sign in to comment.