Skip to content

Commit c608603

Browse files
authored
MetalBlitter: add support for blitting integer formats (#9045)
1 parent f5c0fab commit c608603

File tree

8 files changed

+284
-24
lines changed

8 files changed

+284
-24
lines changed

filament/backend/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,12 @@ if (APPLE OR LINUX)
576576
test/test_CommandBufferQueue.cpp
577577
test/test_Template.cpp
578578
)
579+
if (APPLE)
580+
# Metal-specific tests
581+
list(APPEND BACKEND_TEST_SRC
582+
test/test_MetalBlitter.mm
583+
)
584+
endif()
579585
set(BACKEND_TEST_LIBS
580586
absl::str_format
581587
backend
@@ -597,6 +603,7 @@ if (APPLE AND NOT IOS)
597603
test/test_RenderExternalImage.cpp)
598604
add_library(backend_test STATIC ${BACKEND_TEST_SRC})
599605
target_link_libraries(backend_test PUBLIC ${BACKEND_TEST_LIBS})
606+
target_compile_options(backend_test PRIVATE "-fobjc-arc")
600607

601608
set(BACKEND_TEST_DEPS
602609
OSDependent

filament/backend/src/metal/MetalBlitter.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,15 @@ class MetalBlitter {
6868
const BlitArgs& args, uint32_t depthPlane);
6969

7070
struct BlitFunctionKey {
71+
enum DataFormat : uint8_t {
72+
FLOAT,
73+
UINT,
74+
INT
75+
};
7176
bool msaaColorSource{};
7277
bool sources3D{};
73-
char padding[2]{};
78+
DataFormat inputFormat{};
79+
DataFormat outputFormat{};
7480

7581
bool isValid() const noexcept {
7682
// MSAA 3D textures do not exist.
@@ -79,8 +85,8 @@ class MetalBlitter {
7985
}
8086

8187
bool operator==(const BlitFunctionKey& rhs) const noexcept {
82-
return msaaColorSource == rhs.msaaColorSource &&
83-
sources3D == rhs.sources3D;
88+
return msaaColorSource == rhs.msaaColorSource && sources3D == rhs.sources3D &&
89+
inputFormat == rhs.inputFormat && outputFormat == rhs.outputFormat;
8490
}
8591
};
8692

filament/backend/src/metal/MetalBlitter.mm

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "MetalBlitter.h"
1818

1919
#include "MetalContext.h"
20+
#include "MetalEnums.h"
2021
#include "MetalUtils.h"
2122

2223
#include <utils/Logger.h>
@@ -28,6 +29,9 @@
2829
#include <metal_stdlib>
2930
#include <simd/simd.h>
3031
32+
#define CAT(a,b) a##b
33+
#define FORMAT_4(x) CAT(x, 4)
34+
3135
using namespace metal;
3236
3337
struct VertexOut
@@ -37,7 +41,7 @@
3741
3842
struct FragmentOut
3943
{
40-
float4 color [[color(0)]];
44+
FORMAT_4(OUTPUT_FORMAT) color [[color(0)]];
4145
};
4246
4347
vertex VertexOut
@@ -66,7 +70,7 @@
6670
#elif SOURCES_3D
6771
texture3d<float, access::sample> sourceColor [[texture(0)]],
6872
#else
69-
texture2d<float, access::sample> sourceColor [[texture(0)]],
73+
texture2d<INPUT_FORMAT, access::sample> sourceColor [[texture(0)]],
7074
#endif // MSAA_COLOR_SOURCE
7175
7276
constant FragmentArgs* args [[buffer(0)]])
@@ -183,9 +187,20 @@ inline bool MTLSizeEqual(T a, T b) noexcept {
183187
[cmdBuffer renderCommandEncoderWithDescriptor:descriptor];
184188
encoder.label = @(label);
185189

186-
BlitFunctionKey key;
190+
191+
auto getDataFormat = [](MTLPixelFormat format) {
192+
if (isMetalFormatUnsignedInteger(format)) {
193+
return BlitFunctionKey::DataFormat::UINT;
194+
} else if (isMetalFormatSignedInteger(format)) {
195+
return BlitFunctionKey::DataFormat::INT;
196+
}
197+
return BlitFunctionKey::DataFormat::FLOAT;
198+
};
199+
BlitFunctionKey key{};
187200
key.msaaColorSource = args.source.texture.textureType == MTLTextureType2DMultisample;
188201
key.sources3D = args.source.texture.textureType == MTLTextureType3D;
202+
key.inputFormat = getDataFormat(args.source.texture.pixelFormat);
203+
key.outputFormat = getDataFormat(args.destination.texture.pixelFormat);
189204
id<MTLFunction> const fragmentFunction = getBlitFragmentFunction(key);
190205

191206
MetalPipelineState const pipelineState {
@@ -311,6 +326,18 @@ inline bool MTLSizeEqual(T a, T b) noexcept {
311326
if (key.sources3D) {
312327
macros[@"SOURCES_3D"] = @"1";
313328
}
329+
auto getDataFormatString = [](BlitFunctionKey::DataFormat f) {
330+
switch (f) {
331+
case BlitFunctionKey::DataFormat::FLOAT:
332+
return @"float";
333+
case BlitFunctionKey::DataFormat::UINT:
334+
return @"uint";
335+
case BlitFunctionKey::DataFormat::INT:
336+
return @"int";
337+
}
338+
};
339+
macros[@"INPUT_FORMAT"] = getDataFormatString(key.inputFormat);
340+
macros[@"OUTPUT_FORMAT"] = getDataFormatString(key.outputFormat);
314341
options.preprocessorMacros = macros;
315342
NSString* const objcSource = [NSString stringWithCString:functionLibrary
316343
encoding:NSUTF8StringEncoding];
@@ -337,11 +364,17 @@ inline bool MTLSizeEqual(T a, T b) noexcept {
337364
return mVertexFunction;
338365
}
339366

367+
MTLCompileOptions* const options = [MTLCompileOptions new];
368+
NSMutableDictionary* const macros = [NSMutableDictionary dictionary];
369+
// these can be anything for the vertex shader
370+
macros[@"INPUT_FORMAT"] = @"float";
371+
macros[@"OUTPUT_FORMAT"] = @"float";
372+
options.preprocessorMacros = macros;
340373
NSString* const objcSource = [NSString stringWithCString:functionLibrary
341374
encoding:NSUTF8StringEncoding];
342375
NSError* error = nil;
343376
id <MTLLibrary> const library = [mContext.device newLibraryWithSource:objcSource
344-
options:nil
377+
options:options
345378
error:&error];
346379

347380
id<MTLFunction> const function = [library newFunctionWithName:@"blitterVertex"];

filament/backend/src/metal/MetalDriver.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ class MetalDriver final : public DriverBase {
5858
public:
5959
static Driver* create(PlatformMetal* platform, const Platform::DriverConfig& driverConfig);
6060

61+
MetalContext* getContext() { return mContext; }
62+
6163
private:
6264

6365
friend class MetalSwapChain;

filament/backend/src/metal/MetalDriver.mm

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1508,14 +1508,6 @@
15081508
<< ") is not supported for "
15091509
"readPixels.";
15101510

1511-
const bool formatConversionNecessary = srcTexture.pixelFormat != format;
1512-
1513-
// TODO: MetalBlitter does not currently support format conversions to integer types.
1514-
// The format and type must match the source pixel format exactly.
1515-
FILAMENT_CHECK_PRECONDITION(!formatConversionNecessary || !isMetalFormatInteger(format))
1516-
<< "readPixels does not support integer format conversions from MTLPixelFormat ("
1517-
<< (int)srcTexture.pixelFormat << ") to (" << (int)format << ").";
1518-
15191511
MTLTextureDescriptor* textureDescriptor =
15201512
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format
15211513
width:srcTextureSize.width

filament/backend/src/metal/MetalEnums.h

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -236,32 +236,47 @@ inline MTLPixelFormat getMetalFormatLinear(MTLPixelFormat format) {
236236
return format;
237237
}
238238

239-
constexpr inline bool isMetalFormatInteger(MTLPixelFormat format) {
239+
constexpr inline bool isMetalFormatUnsignedInteger(MTLPixelFormat format) {
240240
switch (format) {
241241
case MTLPixelFormatR8Uint:
242-
case MTLPixelFormatR8Sint:
243242
case MTLPixelFormatR16Uint:
244-
case MTLPixelFormatR16Sint:
245243
case MTLPixelFormatRG8Uint:
246-
case MTLPixelFormatRG8Sint:
247244
case MTLPixelFormatR32Uint:
248-
case MTLPixelFormatR32Sint:
249245
case MTLPixelFormatRG16Uint:
250-
case MTLPixelFormatRG16Sint:
251246
case MTLPixelFormatRGBA8Uint:
252-
case MTLPixelFormatRGBA8Sint:
253247
case MTLPixelFormatRGB10A2Uint:
254248
case MTLPixelFormatRG32Uint:
255-
case MTLPixelFormatRG32Sint:
256249
case MTLPixelFormatRGBA16Uint:
257-
case MTLPixelFormatRGBA16Sint:
258250
case MTLPixelFormatRGBA32Uint:
251+
return true;
252+
253+
default:
254+
return false;
255+
}
256+
return false;
257+
}
258+
259+
constexpr inline bool isMetalFormatSignedInteger(MTLPixelFormat format) {
260+
switch (format) {
261+
case MTLPixelFormatR8Sint:
262+
case MTLPixelFormatR16Sint:
263+
case MTLPixelFormatRG8Sint:
264+
case MTLPixelFormatR32Sint:
265+
case MTLPixelFormatRG16Sint:
266+
case MTLPixelFormatRGBA8Sint:
267+
case MTLPixelFormatRG32Sint:
268+
case MTLPixelFormatRGBA16Sint:
259269
case MTLPixelFormatRGBA32Sint:
260270
return true;
261271

262272
default:
263273
return false;
264274
}
275+
return false;
276+
}
277+
278+
constexpr inline bool isMetalFormatInteger(MTLPixelFormat format) {
279+
return isMetalFormatUnsignedInteger(format) || isMetalFormatSignedInteger(format);
265280
}
266281

267282
constexpr inline bool isMetalFormatStencil(MTLPixelFormat format) {

filament/backend/test/Skip.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ do {
3232
} \
3333
} while (false)
3434

35+
#define SKIP_IF_NOT(skipEnvironment, rationale) \
36+
do { \
37+
SkipEnvironment skip(skipEnvironment); \
38+
if (!skip.matches()) { \
39+
GTEST_SKIP() << "Skipping test as the " << skip.describe() << "\n" \
40+
<< " This test can't run there because " << rationale; \
41+
} \
42+
} while (false)
43+
3544
#define NONFATAL_FAIL_IF(skipEnvironment, rationale) \
3645
do { \
3746
SkipEnvironment skip(skipEnvironment); \

0 commit comments

Comments
 (0)