examples: Add imageformats example (#2872)

* examples: Add imageformats example

Example which renders RGBA color wheel, alpha gradient and full value channel textures to
catch conversion errors and bad internal type representations between different backend
renderers.

The example uses bimg to convert from a master texture to different textures.

Each texture is rendered in an ImGui list using `ImGui::Image`. A close-up is visible
when hovering over each image.

U and I textures are skipped because they don't work for sampling.
Compressed textures are skipped because they don't have conversion functions.
Depth textures are not rendered because they don't work with channels.

* pixelformats: Present texture format in table

* Update pixelformats.cpp

* Update pixelformats.cpp

* pixelformats: Update screenshot

* pixelformats: Add checkerboard background

Co-authored-by: Michał Cichoń <michcic@gmail.com>
Co-authored-by: Бранимир Караџић <branimirkaradzic@gmail.com>
This commit is contained in:
Sandy 2022-08-22 15:16:58 -04:00 committed by GitHub
parent 2d313f46ad
commit 651432df1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 343 additions and 0 deletions

View File

@ -0,0 +1,10 @@
#
# Copyright 2011-2022 Branimir Karadzic. All rights reserved.
# License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE
#
BGFX_DIR=../..
RUNTIME_DIR=$(BGFX_DIR)/examples/runtime
BUILD_DIR=../../.build
include $(BGFX_DIR)/scripts/shader.mk

View File

@ -0,0 +1,330 @@
/*
* Copyright 2022-2022 Sandy Carter. All rights reserved.
* License: https://github.com/bkaradzic/bgfx/blob/master/LICENSE
*/
#include "common.h"
#include "bgfx_utils.h"
#include "imgui/imgui.h"
#include <bx/allocator.h>
#include <bx/math.h>
namespace
{
const int TEXTURE_SIZE = 256;
const int HALF_TEXTURE_SIZE = TEXTURE_SIZE / 2;
const int NUM_FORMATS = bgfx::TextureFormat::UnknownDepth - bgfx::TextureFormat::Unknown - 1;
const int NUM_FORMATS_IN_ROW = (int)bx::ceil(bx::sqrt(NUM_FORMATS));
class ExamplePixelFormats : public entry::AppI
{
public:
ExamplePixelFormats(const char* _name, const char* _description, const char* _url)
: entry::AppI(_name, _description, _url)
{
}
void init(int32_t _argc, const char* const* _argv, uint32_t _width, uint32_t _height) override
{
Args args(_argc, _argv);
m_width = _width;
m_height = _height;
m_debug = BGFX_DEBUG_TEXT;
m_reset = BGFX_RESET_VSYNC;
bgfx::Init init;
init.type = args.m_type;
init.vendorId = args.m_pciId;
init.resolution.width = m_width;
init.resolution.height = m_height;
init.resolution.reset = m_reset;
bgfx::init(init);
// Enable debug text.
bgfx::setDebug(m_debug);
// Set view 0 clear state.
bgfx::setViewClear(0
, BGFX_CLEAR_COLOR|BGFX_CLEAR_DEPTH
, 0x303030ff
, 1.0f
, 0
);
const uint32_t flags = 0
| BGFX_SAMPLER_U_CLAMP
| BGFX_SAMPLER_V_CLAMP
| BGFX_SAMPLER_MIN_POINT
| BGFX_SAMPLER_MAG_POINT
;
float* rgbaf32Pixels = (float*)BX_ALLOC(entry::getAllocator(), TEXTURE_SIZE * TEXTURE_SIZE * 4 * sizeof(float));
int x, y;
for (y = 0 ; y < TEXTURE_SIZE; ++y)
{
for (x = 0; x < TEXTURE_SIZE; ++x)
{
float relX = (x - HALF_TEXTURE_SIZE) / (float) HALF_TEXTURE_SIZE;
float relY = (y - HALF_TEXTURE_SIZE) / (float) HALF_TEXTURE_SIZE;
float distance = bx::min(1.0f, bx::sqrt(relX * relX + relY * relY));
float angle = bx::atan2(relY, relX);
float* pixel = &rgbaf32Pixels[(x + y * TEXTURE_SIZE) * 4];
float hsv[3] = {angle / (2.0f * bx::kPi), 1.0f, 1.0f - distance};
bx::hsvToRgb(pixel, hsv);
pixel[3] = 1.0f - distance;
}
}
for (y = 0; y < 16; ++y)
{
for (x = 0; x < 16; ++x)
{
float* r = &rgbaf32Pixels[(x + (TEXTURE_SIZE - 36 + y) * TEXTURE_SIZE) * 4];
r[0] = 1.0f;
r[1] = 0.0f;
r[2] = 0.0f;
r[3] = 1.0f;
float* g = &rgbaf32Pixels[(x + 16 + (TEXTURE_SIZE - 36 + y) * TEXTURE_SIZE) * 4];
g[0] = 0.0f;
g[1] = 1.0f;
g[2] = 0.0f;
g[3] = 1.0f;
float* b = &rgbaf32Pixels[(x + 32 + (TEXTURE_SIZE - 36 + y) * TEXTURE_SIZE) * 4];
b[0] = 0.0f;
b[1] = 0.0f;
b[2] = 1.0f;
b[3] = 1.0f;
}
}
for (y = 0; y < 16; ++y)
{
for (x = 0; x < 48; ++x)
{
float* a = &rgbaf32Pixels[(x + (TEXTURE_SIZE - 20 + y) * TEXTURE_SIZE) * 4];
a[0] = 1.0f;
a[1] = 1.0f;
a[2] = 1.0f;
a[3] = 1.0f - (float)x / 48.0f;
}
}
bimg::TextureInfo info;
for (int i = 0; i < NUM_FORMATS; ++i)
{
int format = (int)bgfx::TextureFormat::Unknown + 1 + i;
const char* formatName = bimg::getName(bimg::TextureFormat::Enum(format));
int32_t formatNameLen = bx::strLen(formatName);
if (!bimg::imageConvert(bimg::TextureFormat::Enum(format), bimg::TextureFormat::RGBA32F)
|| formatName[formatNameLen - 1] == 'I'
|| formatName[formatNameLen - 1] == 'U'
)
{
m_textures[i] = BGFX_INVALID_HANDLE;
continue;
}
bimg::imageGetSize(&info, TEXTURE_SIZE, TEXTURE_SIZE, 1, false, false, 1, bimg::TextureFormat::Enum(format));
const bgfx::Memory* mem = bgfx::alloc(info.storageSize);
bimg::imageConvert(entry::getAllocator(), mem->data, info.format, rgbaf32Pixels, bimg::TextureFormat::RGBA32F, TEXTURE_SIZE, TEXTURE_SIZE, 1);
m_textures[i] = bgfx::createTexture2D(info.width, info.height, info.numMips > 1, info.numLayers, bgfx::TextureFormat::Enum(info.format), flags, mem);
bgfx::setName(m_textures[i], formatName);
bgfx::setViewName(bgfx::ViewId(i + 1), formatName);
}
const bgfx::Memory* checkerboardImageMemory = bgfx::alloc(TEXTURE_SIZE * TEXTURE_SIZE * 4);
bimg::imageCheckerboard(checkerboardImageMemory->data, TEXTURE_SIZE, TEXTURE_SIZE, 16, 0xFF909090, 0xFF707070);
m_checkerboard = bgfx::createTexture2D(TEXTURE_SIZE, TEXTURE_SIZE, false, 1, bgfx::TextureFormat::RGBA8, flags, checkerboardImageMemory);
BX_FREE(entry::getAllocator(), rgbaf32Pixels);
imguiCreate();
}
int shutdown() override
{
imguiDestroy();
for (auto texture: m_textures)
{
if (bgfx::isValid(texture))
bgfx::destroy(texture);
}
if (bgfx::isValid(m_checkerboard))
bgfx::destroy(m_checkerboard);
// Shutdown bgfx.
bgfx::shutdown();
return 0;
}
void imguiTextBoxUnformatted(const ImVec2& size, const char* text) const
{
ImVec2 textSize = ImGui::CalcTextSize(text);
ImVec2 textOffset = ImVec2(
size.x >= 0.0f ? bx::max((size.x - textSize.x) / 2, 0.0f) : 0.0f,
size.y >= 0.0f ? bx::max((size.y - textSize.y) / 2, 0.0f) : 0.0f
);
ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPosX() + textOffset.x, ImGui::GetCursorPosY() + textOffset.y));
ImGui::TextUnformatted(text);
};
void imguiTexturePreview(const ImVec2& size, bgfx::TextureHandle texture, const ImVec2& previewSizeHint = ImVec2(-1.0f, -1.0f)) const
{
ImVec2 origin = ImGui::GetCursorScreenPos();
ImVec2 previewSize = ImVec2(
previewSizeHint.x >= 0.0f ? previewSizeHint.x : size.x,
previewSizeHint.y >= 0.0f ? previewSizeHint.y : size.y
);
ImVec2 previewPos = ImVec2(
origin.x + bx::max(0.0f, (size.x - previewSize.x) / 2),
origin.y + bx::max(0.0f, (size.y - previewSize.y) / 2)
);
if (bgfx::isValid(texture))
{
if (bgfx::isValid(m_checkerboard))
{
ImGui::SetCursorScreenPos(previewPos);
ImGui::Image(m_checkerboard, previewSize);
}
ImGui::SetCursorScreenPos(previewPos);
ImGui::Image(texture, previewSize);
}
else
{
ImU32 color = ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.25f);
ImDrawList* drawList = ImGui::GetWindowDrawList();
ImVec2 p0 = previewPos;
ImVec2 p1 = ImVec2(p0.x + previewSize.x, p0.y + previewSize.y);
drawList->AddRect(p0, p1, color);
drawList->AddLine(p0, p1, color);
// imguiTextBoxUnformatted(size, "INVALID");
}
ImGui::SetCursorScreenPos(origin);
ImGui::Dummy(size);
};
bool update() override
{
if (!entry::processEvents(m_width, m_height, m_debug, m_reset, &m_mouseState) )
{
imguiBeginFrame(m_mouseState.m_mx
, m_mouseState.m_my
, (m_mouseState.m_buttons[entry::MouseButton::Left ] ? IMGUI_MBUT_LEFT : 0)
| (m_mouseState.m_buttons[entry::MouseButton::Right ] ? IMGUI_MBUT_RIGHT : 0)
| (m_mouseState.m_buttons[entry::MouseButton::Middle] ? IMGUI_MBUT_MIDDLE : 0)
, m_mouseState.m_mz
, uint16_t(m_width)
, uint16_t(m_height)
);
showExampleDialog(this);
ImGui::SetNextWindowPos(ImVec2(360.0f, 40.0f), ImGuiCond_FirstUseEver);
ImGui::Begin("Formats", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
float cellWidth = m_previewSize;
for (int i = 0; i < NUM_FORMATS; ++i)
{
int format = (int)bgfx::TextureFormat::Unknown + 1 + i;
ImVec2 textSize = ImGui::CalcTextSize(bimg::getName(bimg::TextureFormat::Enum(format)));
cellWidth = bx::max(cellWidth, textSize.x);
}
ImDrawList* drawList = ImGui::GetWindowDrawList();
ImGui::DragFloat("Preview Size", &m_previewSize, 1.0f, 10.0f, TEXTURE_SIZE);
ImGui::BeginTable("Formats", NUM_FORMATS_IN_ROW, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit);
for (int i = 0; i < NUM_FORMATS; ++i)
{
ImGui::TableNextColumn();
int format = (int)bgfx::TextureFormat::Unknown + 1 + i;
bool isSelected = (m_selectedFormat == format);
ImDrawListSplitter splitter;
splitter.Split(drawList, 2);
splitter.SetCurrentChannel(drawList, 1);
ImGui::BeginGroup();
ImGuiTextBuffer label;
label.append(bimg::getName(bimg::TextureFormat::Enum(format)));
imguiTextBoxUnformatted(ImVec2(cellWidth, 0.0f), label.c_str());
imguiTexturePreview(ImVec2(cellWidth, m_previewSize), m_textures[i], ImVec2(m_previewSize, m_previewSize));
ImGui::EndGroup();
splitter.SetCurrentChannel(drawList, 0);
ImGui::SetCursorScreenPos(ImGui::GetItemRectMin());
ImGui::PushID(i);
if (ImGui::Selectable("##selectable", &isSelected, ImGuiSelectableFlags_AllowItemOverlap, ImGui::GetItemRectSize()))
m_selectedFormat = bimg::TextureFormat::Enum(format);
ImGui::PopID();
splitter.Merge(drawList);
}
ImGui::EndTable();
ImGui::End();
ImGui::SetNextWindowPos(ImVec2(40.0f, 300.0f), ImGuiCond_FirstUseEver);
ImGui::Begin("Selected Format", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::Text("Format: %s", bimg::getName(m_selectedFormat));
int selectedTextureIndex = m_selectedFormat - 1 - (int)bimg::TextureFormat::Unknown;
bgfx::TextureHandle selectedTexture = BGFX_INVALID_HANDLE;
if (m_selectedFormat != bimg::TextureFormat::Unknown)
selectedTexture = m_textures[selectedTextureIndex];
imguiTexturePreview(ImVec2(TEXTURE_SIZE, TEXTURE_SIZE), selectedTexture);
ImGui::End();
imguiEndFrame();
// Set view 0 default viewport.
bgfx::setViewRect(0, 0, 0, uint16_t(m_width), uint16_t(m_height) );
// This dummy draw call is here to make sure that view 0 is cleared
// if no other draw calls are submitted to viewZ 0.
bgfx::touch(0);
// Advance to next frame. Rendering thread will be kicked to
// process submitted rendering primitives.
bgfx::frame();
return true;
}
return false;
}
entry::MouseState m_mouseState;
uint32_t m_width;
uint32_t m_height;
uint32_t m_debug;
uint32_t m_reset;
float m_previewSize = 50.0f;
bimg::TextureFormat::Enum m_selectedFormat = bimg::TextureFormat::Unknown;
bgfx::TextureHandle m_checkerboard = BGFX_INVALID_HANDLE;
bgfx::TextureHandle m_textures[NUM_FORMATS];
};
} // namespace
ENTRY_IMPLEMENT_MAIN(
ExamplePixelFormats
, "47-pixelformats"
, "Texture formats."
, "https://bkaradzic.github.io/bgfx/examples.html#pixelformats"
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

View File

@ -50,6 +50,7 @@ all:
@make -s --no-print-directory build -C 44-sss
@make -s --no-print-directory build -C 45-bokeh
@make -s --no-print-directory build -C 46-fsr
@make -s --no-print-directory build -C 47-pixelformats
rebuild:
@make -s --no-print-directory rebuild -C 01-cubes
@ -98,6 +99,7 @@ rebuild:
@make -s --no-print-directory rebuild -C 44-sss
@make -s --no-print-directory rebuild -C 45-bokeh
@make -s --no-print-directory rebuild -C 46-fsr
@make -s --no-print-directory rebuild -C 47-pixelformats
rebuild-embedded:
@make -s --no-print-directory rebuild -C 02-metaballs

View File

@ -590,6 +590,7 @@ or _OPTIONS["with-combined-examples"] then
, "44-sss"
, "45-bokeh"
, "46-fsr"
, "47-pixelformats"
)
-- 17-drawstress requires multithreading, does not compile for singlethreaded wasm