Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query pixel format bytes-per-pixel, row- and surface-pitch. #962

Merged
merged 6 commits into from
Jan 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
## Updates

#### 06-Jan-2024

> NOTE: if you use sokol_gfx.h and sokol_app.h together, make sure to update both. This is
because the pixel format enum in sokol_gfx.h has been shuffled around a bit, and as a result, some internal
pixel format constants in sokol_app.h had to move too!

- sokol_gfx.h: some minor non-breaking features:
- the struct `sg_pixel_format` has two no items:
- `bool compressed`: true if this is a hardware-compressed pixel format
- `int bytes_per_pixel`: as the name says, with the caveats that this is
zero for compressed formats (because the smallest element in compressed format is a block)
- two previously private helper functions have been exposed to help with size computations
for texture data, these may be useful when preparing image data for consumption by `sg_make_image()`
and `sg_update_image()`:
- `int sg_query_row_pitch(sg_pixel_format fmt, int width, int row_align_bytes)`:
Computes the number of bytes in a texture row for a given pixel format. A 'row' has
different meanings for uncompressed vs compressed formats: For uncompressed pixel
formats, a row is a single line of pixels, while for compressed formats, a row is
a line of 'compression blocks'. `width` is always in pixels.
- `int sg_query_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align_bytes)`:
Computes number of bytes in a texture surface (e.g. a single mipmap) for a given
pixel format. `width` and `hight` are always in pixels.

The `row_align_bytes` parammeter is for added flexibility. For image data that goes into
the `sg_make_image()` or `sg_update_image()` this should generally be 1, because these
functions take tightly packed image data as input no matter what alignment restrictions
exist in the backend 3D APIs.
- Related issue: https://github.com/floooh/sokol/issues/946, and PR: https://github.com/floooh/sokol/pull/962

#### 03-Jan-2024

- sokol_nuklear.h: `snk_handle_event()` now returns a bool to indicate whether the
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Simple
[STB-style](https://github.com/nothings/stb/blob/master/docs/stb_howto.txt)
cross-platform libraries for C and C++, written in C.

[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**26-Oct-2023** IMPORTANT REGRESSION FIX IN sokol_app.h FOR GL ON WINDOWS!)
[**See what's new**](https://github.com/floooh/sokol/blob/master/CHANGELOG.md) (**06-Jan-2024**: some new size computation helper functions in sokol_gfx.h)

[![Build](/../../actions/workflows/main.yml/badge.svg)](/../../actions/workflows/main.yml) [![Bindings](/../../actions/workflows/gen_bindings.yml/badge.svg)](/../../actions/workflows/gen_bindings.yml) [![build](https://github.com/floooh/sokol-zig/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-zig/actions/workflows/main.yml) [![build](https://github.com/floooh/sokol-nim/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-nim/actions/workflows/main.yml) [![Odin](https://github.com/floooh/sokol-odin/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-odin/actions/workflows/main.yml)[![Rust](https://github.com/floooh/sokol-rust/actions/workflows/main.yml/badge.svg)](https://github.com/floooh/sokol-rust/actions/workflows/main.yml)

Expand Down
4 changes: 2 additions & 2 deletions sokol_app.h
Original file line number Diff line number Diff line change
Expand Up @@ -1894,8 +1894,8 @@ inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); }
// NOTE: the pixel format values *must* be compatible with sg_pixel_format
#define _SAPP_PIXELFORMAT_RGBA8 (23)
#define _SAPP_PIXELFORMAT_BGRA8 (28)
#define _SAPP_PIXELFORMAT_DEPTH (42)
#define _SAPP_PIXELFORMAT_DEPTH_STENCIL (43)
#define _SAPP_PIXELFORMAT_DEPTH (43)
#define _SAPP_PIXELFORMAT_DEPTH_STENCIL (44)

#if defined(_SAPP_MACOS) || defined(_SAPP_IOS)
// this is ARC compatible
Expand Down
115 changes: 91 additions & 24 deletions sokol_gfx.h
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,25 @@

sg_backend sg_query_backend(void)

--- call the following helper functions to compute the number of
bytes in a texture row or surface for a specific pixel format.
These functions might be helpful when preparing image data for consumption
by sg_make_image() or sg_update_image():

int sg_query_row_pitch(sg_pixel_format fmt, int width, int int row_align_bytes);
int sg_query_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align_bytes);

Width and height are generally in number pixels, but note that 'row' has different meaning
for uncompressed vs compressed pixel formats: for uncompressed formats, a row is identical
with a single line if pixels, while in compressed formats, one row is a line of *compression blocks*.

This is why calling sg_query_surface_pitch() for a compressed pixel format and height
N, N+1, N+2, ... may return the same result.

The row_align_bytes parammeter is for added flexibility. For image data that goes into
the sg_make_image() or sg_update_image() this should generally be 1, because these
functions take tightly packed image data as input no matter what alignment restrictions
exist in the backend 3D APIs.

ON INITIALIZATION:
==================
Expand Down Expand Up @@ -1528,7 +1547,7 @@ typedef enum sg_backend {

Not all pixel formats can be used for everything, call sg_query_pixelformat()
to inspect the capabilities of a given pixelformat. The function returns
an sg_pixelformat_info struct with the following bool members:
an sg_pixelformat_info struct with the following members:

- sample: the pixelformat can be sampled as texture at least with
nearest filtering
Expand All @@ -1540,6 +1559,8 @@ typedef enum sg_backend {
- msaa: multisample-antialiasing is supported when using the
pixelformat for render targets
- depth: the pixelformat can be used for depth-stencil attachments
- compressed: this is a block-compressed format
- bytes_per_pixel: the numbers of bytes in a pixel (0 for compressed formats)

The default pixel format for texture images is SG_PIXELFORMAT_RGBA8.

Expand Down Expand Up @@ -1587,6 +1608,7 @@ typedef enum sg_pixel_format {
SG_PIXELFORMAT_BGRA8,
SG_PIXELFORMAT_RGB10A2,
SG_PIXELFORMAT_RG11B10F,
SG_PIXELFORMAT_RGB9E5,

SG_PIXELFORMAT_RG32UI,
SG_PIXELFORMAT_RG32SI,
Expand All @@ -1601,9 +1623,11 @@ typedef enum sg_pixel_format {
SG_PIXELFORMAT_RGBA32SI,
SG_PIXELFORMAT_RGBA32F,

// NOTE: when adding/removing pixel formats before DEPTH, also update sokol_app.h/SAPP_PIXELFORMAT_*
SG_PIXELFORMAT_DEPTH,
SG_PIXELFORMAT_DEPTH_STENCIL,

// NOTE: don't put any new compressed format in front of here
SG_PIXELFORMAT_BC1_RGBA,
SG_PIXELFORMAT_BC2_RGBA,
SG_PIXELFORMAT_BC3_RGBA,
Expand All @@ -1624,8 +1648,6 @@ typedef enum sg_pixel_format {
SG_PIXELFORMAT_ETC2_RG11,
SG_PIXELFORMAT_ETC2_RG11SN,

SG_PIXELFORMAT_RGB9E5,

_SG_PIXELFORMAT_NUM,
_SG_PIXELFORMAT_FORCE_U32 = 0x7FFFFFFF
} sg_pixel_format;
Expand All @@ -1635,12 +1657,14 @@ typedef enum sg_pixel_format {
by sg_query_pixelformat().
*/
typedef struct sg_pixelformat_info {
bool sample; // pixel format can be sampled in shaders at least with nearest filtering
bool filter; // pixel format can be sampled with linear filtering
bool render; // pixel format can be used as render target
bool blend; // alpha-blending is supported
bool msaa; // pixel format can be used as MSAA render target
bool depth; // pixel format is a depth format
bool sample; // pixel format can be sampled in shaders at least with nearest filtering
bool filter; // pixel format can be sampled with linear filtering
bool render; // pixel format can be used as render target
bool blend; // alpha-blending is supported
bool msaa; // pixel format can be used as MSAA render target
bool depth; // pixel format is a depth format
bool compressed; // true if this is a hardware-compressed format
int bytes_per_pixel; // NOTE: this is 0 for compressed formats, use sg_query_row_pitch() / sg_query_surface_pitch() as alternative
} sg_pixelformat_info;

/*
Expand Down Expand Up @@ -3751,6 +3775,8 @@ SOKOL_GFX_API_DECL sg_backend sg_query_backend(void);
SOKOL_GFX_API_DECL sg_features sg_query_features(void);
SOKOL_GFX_API_DECL sg_limits sg_query_limits(void);
SOKOL_GFX_API_DECL sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt);
SOKOL_GFX_API_DECL int sg_query_row_pitch(sg_pixel_format fmt, int width, int row_align_bytes);
SOKOL_GFX_API_DECL int sg_query_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align_bytes);
// get current state of a resource (INITIAL, ALLOC, VALID, FAILED, INVALID)
SOKOL_GFX_API_DECL sg_resource_state sg_query_buffer_state(sg_buffer buf);
SOKOL_GFX_API_DECL sg_resource_state sg_query_image_state(sg_image img);
Expand Down Expand Up @@ -5582,6 +5608,15 @@ typedef struct {
_sg_sampler_t* fs_smps[SG_MAX_SHADERSTAGE_SAMPLERS];
} _sg_bindings_t;

typedef struct {
bool sample;
bool filter;
bool render;
bool blend;
bool msaa;
bool depth;
} _sg_pixelformat_info_t;

typedef struct {
bool valid;
sg_desc desc; // original desc with default values patched in
Expand All @@ -5599,7 +5634,7 @@ typedef struct {
sg_backend backend;
sg_features features;
sg_limits limits;
sg_pixelformat_info formats[_SG_PIXELFORMAT_NUM];
_sg_pixelformat_info_t formats[_SG_PIXELFORMAT_NUM];
bool stats_enabled;
sg_frame_stats stats;
sg_frame_stats prev_stats;
Expand Down Expand Up @@ -5977,6 +6012,9 @@ _SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) {
case SG_PIXELFORMAT_RGBA32SI:
case SG_PIXELFORMAT_RGBA32F:
return 16;
case SG_PIXELFORMAT_DEPTH:
case SG_PIXELFORMAT_DEPTH_STENCIL:
return 4;
default:
SOKOL_UNREACHABLE;
return 0;
Expand Down Expand Up @@ -6113,61 +6151,61 @@ _SOKOL_PRIVATE int _sg_surface_pitch(sg_pixel_format fmt, int width, int height,
}

// capability table pixel format helper functions
_SOKOL_PRIVATE void _sg_pixelformat_all(sg_pixelformat_info* pfi) {
_SOKOL_PRIVATE void _sg_pixelformat_all(_sg_pixelformat_info_t* pfi) {
pfi->sample = true;
pfi->filter = true;
pfi->blend = true;
pfi->render = true;
pfi->msaa = true;
}

_SOKOL_PRIVATE void _sg_pixelformat_s(sg_pixelformat_info* pfi) {
_SOKOL_PRIVATE void _sg_pixelformat_s(_sg_pixelformat_info_t* pfi) {
pfi->sample = true;
}

_SOKOL_PRIVATE void _sg_pixelformat_sf(sg_pixelformat_info* pfi) {
_SOKOL_PRIVATE void _sg_pixelformat_sf(_sg_pixelformat_info_t* pfi) {
pfi->sample = true;
pfi->filter = true;
}

_SOKOL_PRIVATE void _sg_pixelformat_sr(sg_pixelformat_info* pfi) {
_SOKOL_PRIVATE void _sg_pixelformat_sr(_sg_pixelformat_info_t* pfi) {
pfi->sample = true;
pfi->render = true;
}

_SOKOL_PRIVATE void _sg_pixelformat_srmd(sg_pixelformat_info* pfi) {
_SOKOL_PRIVATE void _sg_pixelformat_srmd(_sg_pixelformat_info_t* pfi) {
pfi->sample = true;
pfi->render = true;
pfi->msaa = true;
pfi->depth = true;
}

_SOKOL_PRIVATE void _sg_pixelformat_srm(sg_pixelformat_info* pfi) {
_SOKOL_PRIVATE void _sg_pixelformat_srm(_sg_pixelformat_info_t* pfi) {
pfi->sample = true;
pfi->render = true;
pfi->msaa = true;
}

_SOKOL_PRIVATE void _sg_pixelformat_sfrm(sg_pixelformat_info* pfi) {
_SOKOL_PRIVATE void _sg_pixelformat_sfrm(_sg_pixelformat_info_t* pfi) {
pfi->sample = true;
pfi->filter = true;
pfi->render = true;
pfi->msaa = true;
}
_SOKOL_PRIVATE void _sg_pixelformat_sbrm(sg_pixelformat_info* pfi) {
_SOKOL_PRIVATE void _sg_pixelformat_sbrm(_sg_pixelformat_info_t* pfi) {
pfi->sample = true;
pfi->blend = true;
pfi->render = true;
pfi->msaa = true;
}

_SOKOL_PRIVATE void _sg_pixelformat_sbr(sg_pixelformat_info* pfi) {
_SOKOL_PRIVATE void _sg_pixelformat_sbr(_sg_pixelformat_info_t* pfi) {
pfi->sample = true;
pfi->blend = true;
pfi->render = true;
}

_SOKOL_PRIVATE void _sg_pixelformat_sfbr(sg_pixelformat_info* pfi) {
_SOKOL_PRIVATE void _sg_pixelformat_sfbr(_sg_pixelformat_info_t* pfi) {
pfi->sample = true;
pfi->filter = true;
pfi->blend = true;
Expand Down Expand Up @@ -9804,7 +9842,7 @@ _SOKOL_PRIVATE void _sg_d3d11_init_caps(void) {
const UINT srv_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_srv_pixel_format((sg_pixel_format)fmt));
const UINT rtv_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_rtv_pixel_format((sg_pixel_format)fmt));
const UINT dsv_dxgi_fmt_caps = _sg_d3d11_dxgi_fmt_caps(_sg_d3d11_dsv_pixel_format((sg_pixel_format)fmt));
sg_pixelformat_info* info = &_sg.formats[fmt];
_sg_pixelformat_info_t* info = &_sg.formats[fmt];
const bool render = 0 != (rtv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_RENDER_TARGET);
const bool depth = 0 != (dsv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_DEPTH_STENCIL);
info->sample = 0 != (srv_dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_TEXTURE2D);
Expand Down Expand Up @@ -16202,7 +16240,7 @@ _SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) {
if (img && img->slot.state == SG_RESOURCESTATE_VALID) {
_SG_VALIDATE(img->cmn.type == stage->images[i].image_type, VALIDATE_ABND_VS_IMAGE_TYPE_MISMATCH);
_SG_VALIDATE(img->cmn.sample_count == 1, VALIDATE_ABND_VS_IMAGE_MSAA);
const sg_pixelformat_info* info = &_sg.formats[img->cmn.pixel_format];
const _sg_pixelformat_info_t* info = &_sg.formats[img->cmn.pixel_format];
switch (stage->images[i].sample_type) {
case SG_IMAGESAMPLETYPE_FLOAT:
_SG_VALIDATE(info->filter, VALIDATE_ABND_VS_EXPECTED_FILTERABLE_IMAGE);
Expand Down Expand Up @@ -16258,7 +16296,7 @@ _SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) {
if (img && img->slot.state == SG_RESOURCESTATE_VALID) {
_SG_VALIDATE(img->cmn.type == stage->images[i].image_type, VALIDATE_ABND_FS_IMAGE_TYPE_MISMATCH);
_SG_VALIDATE(img->cmn.sample_count == 1, VALIDATE_ABND_FS_IMAGE_MSAA);
const sg_pixelformat_info* info = &_sg.formats[img->cmn.pixel_format];
const _sg_pixelformat_info_t* info = &_sg.formats[img->cmn.pixel_format];
switch (stage->images[i].sample_type) {
case SG_IMAGESAMPLETYPE_FLOAT:
_SG_VALIDATE(info->filter, VALIDATE_ABND_FS_EXPECTED_FILTERABLE_IMAGE);
Expand Down Expand Up @@ -17048,7 +17086,36 @@ SOKOL_API_IMPL sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt) {
SOKOL_ASSERT(_sg.valid);
int fmt_index = (int) fmt;
SOKOL_ASSERT((fmt_index > SG_PIXELFORMAT_NONE) && (fmt_index < _SG_PIXELFORMAT_NUM));
return _sg.formats[fmt_index];
const _sg_pixelformat_info_t* src = &_sg.formats[fmt_index];
sg_pixelformat_info res;
_sg_clear(&res, sizeof(res));
res.sample = src->sample;
res.filter = src->filter;
res.render = src->render;
res.blend = src->blend;
res.msaa = src->msaa;
res.depth = src->depth;
res.compressed = _sg_is_compressed_pixel_format(fmt);
if (!res.compressed) {
res.bytes_per_pixel = _sg_pixelformat_bytesize(fmt);
}
return res;
}

SOKOL_API_IMPL int sg_query_row_pitch(sg_pixel_format fmt, int width, int row_align_bytes) {
SOKOL_ASSERT(_sg.valid);
SOKOL_ASSERT(width > 0);
SOKOL_ASSERT((row_align_bytes > 0) && _sg_ispow2(row_align_bytes));
SOKOL_ASSERT(((int)fmt > SG_PIXELFORMAT_NONE) && ((int)fmt < _SG_PIXELFORMAT_NUM));
return _sg_row_pitch(fmt, width, row_align_bytes);
}

SOKOL_API_IMPL int sg_query_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align_bytes) {
SOKOL_ASSERT(_sg.valid);
SOKOL_ASSERT((width > 0) && (height > 0));
SOKOL_ASSERT((row_align_bytes > 0) && _sg_ispow2(row_align_bytes));
SOKOL_ASSERT(((int)fmt > SG_PIXELFORMAT_NONE) && ((int)fmt < _SG_PIXELFORMAT_NUM));
return _sg_surface_pitch(fmt, width, height, row_align_bytes);
}

SOKOL_API_IMPL sg_frame_stats sg_query_frame_stats(void) {
Expand Down