Skip to content

Commit 9f79108

Browse files
authored
Merge pull request #243 from defold/9.0.x-astc-support-webgpu
Add ASTC compressed texture support for WebGPU
2 parents 53bbca2 + b78b453 commit 9f79108

File tree

8 files changed

+203
-1
lines changed

8 files changed

+203
-1
lines changed

defold-rive/commonsrc/atlas.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <common/atlas.h>
1414
#include <common/factory.h>
1515
#include <common/types.h>
16+
#include <common/astc.h>
1617

1718
#include <dmsdk/dlib/dstrings.h>
1819
#include <dmsdk/dlib/hash.h>
@@ -207,6 +208,12 @@ namespace dmRive {
207208

208209
rive::rcp<rive::RenderImage> LoadImageFromMemory(HRenderContext context, void* resource, uint32_t resource_size)
209210
{
211+
// Auto-detect ASTC format
212+
if (IsASTCData((const uint8_t*)resource, resource_size))
213+
{
214+
DEBUGLOG("Loading ASTC texture (%u bytes)", resource_size);
215+
return CreateRiveRenderImageASTC(context, resource, resource_size);
216+
}
210217
return CreateRiveRenderImage(context, resource, resource_size);
211218
}
212219

@@ -277,7 +284,7 @@ namespace dmRive {
277284

278285
if (!image)
279286
{
280-
image = CreateRiveRenderImage(m_RiveRenderContext, (void*) inBandBytes.data(), inBandBytes.size());
287+
image = LoadImageFromMemory(m_RiveRenderContext, (void*) inBandBytes.data(), inBandBytes.size());
281288
DEBUGLOG(" In band asset: file: '%s' data: %u bytes", name.c_str(), (uint32_t)inBandBytes.size());
282289
}
283290

defold-rive/include/common/astc.h

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright 2020 The Defold Foundation
2+
// Licensed under the Defold License version 1.0 (the "License"); you may not use
3+
// this file except in compliance with the License.
4+
//
5+
// You may obtain a copy of the License, together with FAQs at
6+
// https://www.defold.com/license
7+
//
8+
// Unless required by applicable law or agreed to in writing, software distributed
9+
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
10+
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
// specific language governing permissions and limitations under the License.
12+
13+
#ifndef DM_RIVE_ASTC_H
14+
#define DM_RIVE_ASTC_H
15+
16+
#include <stdint.h>
17+
#include <stddef.h>
18+
19+
namespace dmRive
20+
{
21+
static const uint32_t ASTC_MAGIC = 0x5CA1AB13;
22+
23+
struct ASTCHeader
24+
{
25+
uint32_t magic;
26+
uint8_t block_width;
27+
uint8_t block_height;
28+
uint8_t block_depth;
29+
uint32_t width;
30+
uint32_t height;
31+
uint32_t depth;
32+
};
33+
34+
// Check if data starts with ASTC magic bytes
35+
inline bool IsASTCData(const uint8_t* data, size_t data_size)
36+
{
37+
if (data_size < 4)
38+
return false;
39+
uint32_t magic = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
40+
return magic == ASTC_MAGIC;
41+
}
42+
43+
// Parse ASTC header from raw data. Returns false if invalid.
44+
inline bool ParseASTCHeader(const uint8_t* data, size_t data_size, ASTCHeader* out)
45+
{
46+
if (data_size < 16 || !IsASTCData(data, data_size))
47+
return false;
48+
49+
out->magic = ASTC_MAGIC;
50+
out->block_width = data[4];
51+
out->block_height = data[5];
52+
out->block_depth = data[6];
53+
54+
// Dimensions are 24-bit little-endian
55+
out->width = data[7] | (data[8] << 8) | (data[9] << 16);
56+
out->height = data[10] | (data[11] << 8) | (data[12] << 16);
57+
out->depth = data[13] | (data[14] << 8) | (data[15] << 16);
58+
59+
return true;
60+
}
61+
62+
// Get pointer to compressed data (after 16-byte header)
63+
inline const uint8_t* GetASTCCompressedData(const uint8_t* data)
64+
{
65+
return data + 16;
66+
}
67+
68+
// Get size of compressed data (total size minus 16-byte header)
69+
inline size_t GetASTCCompressedDataSize(size_t total_size)
70+
{
71+
return total_size > 16 ? total_size - 16 : 0;
72+
}
73+
}
74+
75+
#endif // DM_RIVE_ASTC_H

defold-rive/include/defold/renderer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ namespace dmRive
4343
HRenderContext NewRenderContext();
4444
void DeleteRenderContext(HRenderContext context);
4545
rive::rcp<rive::RenderImage> CreateRiveRenderImage(HRenderContext context, void* bytes, uint32_t byte_count);
46+
rive::rcp<rive::RenderImage> CreateRiveRenderImageASTC(HRenderContext context, void* bytes, uint32_t byte_count);
4647
rive::Factory* GetRiveFactory(HRenderContext context);
4748
rive::Renderer* GetRiveRenderer(HRenderContext context);
4849
rive::Mat2D GetViewTransform(HRenderContext context, dmRender::HRenderContext render_context);

defold-rive/src/private/renderer_context.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ namespace dmRive
2222

2323
virtual dmGraphics::HTexture GetBackingTexture() = 0;
2424
virtual rive::rcp<rive::gpu::Texture> MakeImageTexture(uint32_t width, uint32_t height, uint32_t mipLevelCount, const uint8_t imageDataRGBA[]) = 0;
25+
virtual rive::rcp<rive::gpu::Texture> MakeImageTextureASTC(uint32_t width, uint32_t height, uint8_t blockW, uint8_t blockH, const uint8_t astcData[], uint32_t astcDataSize) = 0;
2526
};
2627

2728
IDefoldRiveRenderer* MakeDefoldRiveRendererMetal();

defold-rive/src/private/renderer_context_metal.mm

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
#include "renderer_context.h"
33

4+
#include <dmsdk/dlib/log.h>
45
#include <rive/renderer/rive_renderer.hpp>
56
#include <rive/renderer/texture.hpp>
67
#include <rive/renderer/metal/render_context_metal_impl.h>
@@ -124,6 +125,18 @@ void SetRenderTargetTexture(dmGraphics::HTexture texture) override
124125
}
125126
}
126127

128+
rive::rcp<rive::gpu::Texture> MakeImageTextureASTC(uint32_t width,
129+
uint32_t height,
130+
uint8_t blockW,
131+
uint8_t blockH,
132+
const uint8_t astcData[],
133+
uint32_t astcDataSize) override
134+
{
135+
// TODO: Implement ASTC texture loading for Metal
136+
dmLogWarning("MakeImageTextureASTC not implemented for Metal");
137+
return nullptr;
138+
}
139+
127140
private:
128141
id<MTLDevice> m_GPU = MTLCreateSystemDefaultDevice();
129142
id<MTLCommandQueue> m_Queue;

defold-rive/src/private/renderer_context_opengl.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,18 @@ namespace dmRive
173173
return texture;
174174
}
175175

176+
rive::rcp<rive::gpu::Texture> MakeImageTextureASTC(uint32_t width,
177+
uint32_t height,
178+
uint8_t blockW,
179+
uint8_t blockH,
180+
const uint8_t astcData[],
181+
uint32_t astcDataSize) override
182+
{
183+
// TODO: Implement ASTC texture loading for OpenGL
184+
dmLogWarning("MakeImageTextureASTC not implemented for OpenGL");
185+
return nullptr;
186+
}
187+
176188
private:
177189

178190
void SetDefoldGraphicsState(dmGraphics::State state, bool flag)

defold-rive/src/private/renderer_context_webgpu.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,78 @@ namespace dmRive
191191
return texture;
192192
}
193193

194+
static wgpu::TextureFormat GetASTCFormat(uint8_t blockW, uint8_t blockH)
195+
{
196+
// ASTC block sizes mapped to WebGPU sRGB formats (correct for color textures).
197+
// NOTE: Linear data (e.g. normal maps) would need Unorm variants instead,
198+
// but ASTC files don't contain color space info. For now we assume sRGB.
199+
if (blockW == 4 && blockH == 4) return wgpu::TextureFormat::ASTC4x4UnormSrgb;
200+
if (blockW == 5 && blockH == 4) return wgpu::TextureFormat::ASTC5x4UnormSrgb;
201+
if (blockW == 5 && blockH == 5) return wgpu::TextureFormat::ASTC5x5UnormSrgb;
202+
if (blockW == 6 && blockH == 5) return wgpu::TextureFormat::ASTC6x5UnormSrgb;
203+
if (blockW == 6 && blockH == 6) return wgpu::TextureFormat::ASTC6x6UnormSrgb;
204+
if (blockW == 8 && blockH == 5) return wgpu::TextureFormat::ASTC8x5UnormSrgb;
205+
if (blockW == 8 && blockH == 6) return wgpu::TextureFormat::ASTC8x6UnormSrgb;
206+
if (blockW == 8 && blockH == 8) return wgpu::TextureFormat::ASTC8x8UnormSrgb;
207+
if (blockW == 10 && blockH == 5) return wgpu::TextureFormat::ASTC10x5UnormSrgb;
208+
if (blockW == 10 && blockH == 6) return wgpu::TextureFormat::ASTC10x6UnormSrgb;
209+
if (blockW == 10 && blockH == 8) return wgpu::TextureFormat::ASTC10x8UnormSrgb;
210+
if (blockW == 10 && blockH == 10) return wgpu::TextureFormat::ASTC10x10UnormSrgb;
211+
if (blockW == 12 && blockH == 10) return wgpu::TextureFormat::ASTC12x10UnormSrgb;
212+
if (blockW == 12 && blockH == 12) return wgpu::TextureFormat::ASTC12x12UnormSrgb;
213+
return wgpu::TextureFormat::Undefined;
214+
}
215+
216+
rive::rcp<rive::gpu::Texture> MakeImageTextureASTC(uint32_t width,
217+
uint32_t height,
218+
uint8_t blockW,
219+
uint8_t blockH,
220+
const uint8_t astcData[],
221+
uint32_t astcDataSize) override
222+
{
223+
wgpu::TextureFormat format = GetASTCFormat(blockW, blockH);
224+
if (format == wgpu::TextureFormat::Undefined)
225+
{
226+
dmLogError("Unsupported ASTC block size %dx%d", blockW, blockH);
227+
return nullptr;
228+
}
229+
230+
// All ASTC blocks are 16 bytes regardless of block dimensions
231+
uint32_t blocksX = (width + blockW - 1) / blockW;
232+
uint32_t blocksY = (height + blockH - 1) / blockH;
233+
uint32_t bytesPerRow = blocksX * 16;
234+
uint32_t expectedSize = blocksX * blocksY * 16;
235+
236+
if (astcDataSize < expectedSize)
237+
{
238+
dmLogError("ASTC data size %u is less than expected %u for %ux%u texture with %dx%d blocks",
239+
astcDataSize, expectedSize, width, height, blockW, blockH);
240+
return nullptr;
241+
}
242+
243+
wgpu::TextureDescriptor textureDesc = {
244+
.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopyDst,
245+
.dimension = wgpu::TextureDimension::e2D,
246+
.size = {width, height},
247+
.format = format,
248+
.mipLevelCount = 1,
249+
};
250+
251+
wgpu::Texture texture = m_Device.CreateTexture(&textureDesc);
252+
253+
wgpu::TexelCopyTextureInfo dest = {.texture = texture};
254+
wgpu::TexelCopyBufferLayout layout = {.bytesPerRow = bytesPerRow};
255+
wgpu::Extent3D extent = {width, height};
256+
257+
m_Queue.WriteTexture(&dest,
258+
astcData,
259+
astcDataSize,
260+
&layout,
261+
&extent);
262+
263+
return rive::make_rcp<rive::gpu::TextureWebGPUImpl>(width, height, std::move(texture));
264+
}
265+
194266
private:
195267
std::unique_ptr<rive::gpu::RenderContext> m_RenderContext;
196268
rive::rcp<rive::gpu::RenderTargetWebGPU> m_RenderTarget;

defold-rive/src/private/renderer_private.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <dmsdk/render/render.h>
66

77
#include "renderer_context.h"
8+
#include <common/astc.h>
89
#include "defold/renderer.h"
910

1011
#include <rive/shapes/image.hpp>
@@ -266,6 +267,26 @@ namespace dmRive
266267
return texture != nullptr ? rive::make_rcp<rive::RiveRenderImage>(std::move(texture)) : nullptr;
267268
}
268269

270+
rive::rcp<rive::RenderImage> CreateRiveRenderImageASTC(HRenderContext context, void* bytes, uint32_t byte_count)
271+
{
272+
DefoldRiveRenderer* renderer = (DefoldRiveRenderer*) context;
273+
274+
ASTCHeader header;
275+
if (!ParseASTCHeader((const uint8_t*)bytes, byte_count, &header))
276+
{
277+
dmLogError("Failed to parse ASTC header");
278+
return nullptr;
279+
}
280+
281+
const uint8_t* astcData = GetASTCCompressedData((const uint8_t*)bytes);
282+
size_t astcDataSize = GetASTCCompressedDataSize(byte_count);
283+
284+
rive::rcp<rive::gpu::Texture> texture = renderer->m_RenderContext->MakeImageTextureASTC(
285+
header.width, header.height, header.block_width, header.block_height, astcData, (uint32_t)astcDataSize);
286+
287+
return texture != nullptr ? rive::make_rcp<rive::RiveRenderImage>(std::move(texture)) : nullptr;
288+
}
289+
269290
rive::Mat2D GetViewTransform(HRenderContext context, dmRender::HRenderContext render_context)
270291
{
271292
const dmVMath::Matrix4& view_matrix = dmRender::GetViewMatrix(render_context);

0 commit comments

Comments
 (0)