Show splash screen when quick starting into a game

This commit is contained in:
Maschell 2024-07-04 17:30:40 +02:00
parent d58ea5aad3
commit 8f52f74c13
15 changed files with 1715 additions and 5 deletions

View File

@ -21,7 +21,7 @@ WUMS_ROOT := $(DEVKITPRO)/wums
#-------------------------------------------------------------------------------
TARGET := 99_autoboot
BUILD := build
SOURCES := source
SOURCES := source source/utils
DATA := data
INCLUDES := source include

View File

@ -52,3 +52,4 @@ docker run -it --rm -v ${PWD}:/project autobootmodule-builder make clean
## Credits
- GaryOderNichts
- Maschell
- Crementif

View File

@ -2,7 +2,9 @@
#include "BootUtils.h"
#include "MenuUtils.h"
#include "logger.h"
#include "utils/SplashScreenDrawer.h"
#include "utils/SplashSoundPlayer.h"
#include "utils/gfx.h"
#include <coreinit/exit.h>
#include <coreinit/foreground.h>
#include <coreinit/memdefaultheap.h>
@ -351,9 +353,25 @@ bool launchQuickStartTitle() {
MCP_Close(handle);
if (err == 0) {
DEBUG_FUNCTION_LINE("Launch %016llX", titleIdToLaunch);
char metaDir[256] = {};
auto res = ACPGetTitleMetaDir(titleIdToLaunch, metaDir, sizeof(metaDir) - 1);
if (res == ACP_RESULT_SUCCESS) {
GfxInit();
{
SplashScreenDrawer splashScreenDrawer(metaDir);
splashScreenDrawer.Draw();
SplashSoundPlayer splashSound(metaDir);
splashSound.Play();
}
GfxShutdown();
} else {
DEBUG_FUNCTION_LINE_WARN("Failed to find assets");
}
ACPAssignTitlePatch(&titleInfo);
_SYSLaunchTitleWithStdArgsInNoSplash(titleIdToLaunch, nullptr);
_SYSLaunchTitleByPathFromLauncher(titleInfo.path, strlen(titleInfo.path));
return true;
} else {
DEBUG_FUNCTION_LINE_WARN("Failed to get title info");
}
DEBUG_FUNCTION_LINE("Launch Wii U Menu!");

View File

@ -1,7 +1,13 @@
#include "logger.h"
#include <coreinit/filesystem_fsa.h>
#include <coreinit/mcp.h>
#include <cstdint>
#include <cstdio>
#include <mocha/mocha.h>
#include <string_view>
#include <sys/stat.h>
#include <vector>
#include <whb/log.h>
bool GetTitleIdOfDisc(uint64_t *titleId, bool *discPresent) {
if (discPresent) {
@ -77,3 +83,26 @@ bool RestoreMLCUpdateDirectory() {
}
return result;
}
bool LoadFileIntoBuffer(std::string_view path, std::vector<uint8_t> &buffer) {
struct stat st {};
if (stat(path.data(), &st) < 0 || !S_ISREG(st.st_mode)) {
DEBUG_FUNCTION_LINE_INFO("\"%s\" doesn't exists", path.data());
return false;
}
FILE *f = fopen(path.data(), "rb");
if (!f) {
return false;
}
buffer.resize(st.st_size);
if (fread(buffer.data(), 1, st.st_size, f) != st.st_size) {
DEBUG_FUNCTION_LINE_WARN("Failed load %s", path.data());
return false;
}
fclose(f);
return true;
}

View File

@ -3,6 +3,8 @@
#include <cstdint>
#include <malloc.h>
#include <memory>
#include <string_view>
#include <vector>
template<class T, class... Args>
std::unique_ptr<T> make_unique_nothrow(Args &&...args) noexcept(noexcept(T(std::forward<Args>(args)...))) {
@ -33,3 +35,5 @@ bool GetTitleIdOfDisc(uint64_t *titleId, bool *discPresent);
bool DeleteMLCUpdateDirectory();
bool RestoreMLCUpdateDirectory();
bool LoadFileIntoBuffer(std::string_view path, std::vector<uint8_t> &buffer);

View File

@ -0,0 +1,446 @@
#include "ShaderSerializer.h"
#include <cstdint>
#include <gx2/sampler.h>
#include <gx2/shaders.h>
#include <gx2r/buffer.h>
#include <malloc.h>
#include <memory>
#include <span>
#include <string>
#include <vector>
/*
* Based on https://github.com/Crementif/UntitledSandGame/blob/e752613ba54ac8f6767a8b37e9ac3f68ca180ad7/source/common/shader_serializer.h
*/
template<typename Type>
static void writeAt(std::vector<uint8_t> &fh, size_t pos, Type value) {
*reinterpret_cast<Type *>(fh.data() + pos) = value;
}
template<typename Type>
static void write(std::vector<uint8_t> &fh, Type value) {
auto pos = fh.size();
fh.resize(pos + sizeof(Type));
*reinterpret_cast<Type *>(fh.data() + pos) = value;
}
static void writeString(std::vector<uint8_t> &fh, const char *str) {
auto pos = fh.size();
auto len = strlen(str) + 1;
fh.resize((static_cast<size_t>(pos + len) + (4 - 1)) & ~(4 - 1));
memcpy(fh.data() + pos, str, len);
}
static void writeGX2RBuffer(std::vector<uint8_t> &fh, GX2RBuffer *buffer) {
write(fh, buffer->flags);
write(fh, buffer->elemSize);
write(fh, buffer->elemCount);
auto pos = fh.size();
fh.resize(pos + buffer->elemSize * buffer->elemCount);
memcpy(fh.data() + pos, buffer->buffer, buffer->elemSize * buffer->elemCount);
}
std::vector<uint8_t> SerializeVertexShader(GX2VertexShader *vertexShader) {
std::vector<uint8_t> data;
// write regs
write(data, vertexShader->regs.sq_pgm_resources_vs);
write(data, vertexShader->regs.vgt_primitiveid_en);
write(data, vertexShader->regs.spi_vs_out_config);
write(data, vertexShader->regs.num_spi_vs_out_id);
for (uint32_t spi_vs : vertexShader->regs.spi_vs_out_id) {
write(data, spi_vs);
}
write(data, vertexShader->regs.pa_cl_vs_out_cntl);
write(data, vertexShader->regs.sq_vtx_semantic_clear);
write(data, vertexShader->regs.num_sq_vtx_semantic);
for (uint32_t sq_vtx : vertexShader->regs.sq_vtx_semantic) {
write(data, sq_vtx);
}
write(data, vertexShader->regs.vgt_strmout_buffer_en);
write(data, vertexShader->regs.vgt_vertex_reuse_block_cntl);
write(data, vertexShader->regs.vgt_hos_reuse_depth);
// write program
write(data, vertexShader->size);
for (uint32_t i = 0; i < vertexShader->size; i++) {
write(data, ((uint8_t *) vertexShader->program)[i]);
}
write(data, vertexShader->mode);
// write uniform blocks
write(data, vertexShader->uniformBlockCount);
for (uint32_t i = 0; i < vertexShader->uniformBlockCount; i++) {
writeString(data, vertexShader->uniformBlocks[i].name);
write(data, vertexShader->uniformBlocks[i].offset);
write(data, vertexShader->uniformBlocks[i].size);
}
// write uniform vars
write(data, vertexShader->uniformVarCount);
for (uint32_t i = 0; i < vertexShader->uniformVarCount; i++) {
writeString(data, vertexShader->uniformVars[i].name);
write(data, vertexShader->uniformVars[i].type);
write(data, vertexShader->uniformVars[i].count);
write(data, vertexShader->uniformVars[i].offset);
write(data, vertexShader->uniformVars[i].block);
}
// write initial values
write(data, vertexShader->initialValueCount);
for (uint32_t i = 0; i < vertexShader->initialValueCount; i++) {
write(data, vertexShader->initialValues[i].value[0]);
write(data, vertexShader->initialValues[i].value[1]);
write(data, vertexShader->initialValues[i].value[2]);
write(data, vertexShader->initialValues[i].value[3]);
write(data, vertexShader->initialValues[i].offset);
}
// write loop vars
write(data, vertexShader->loopVarCount);
for (uint32_t i = 0; i < vertexShader->loopVarCount; i++) {
write(data, vertexShader->loopVars[i].offset);
write(data, vertexShader->loopVars[i].value);
}
// write sampler vars
write(data, vertexShader->samplerVarCount);
for (uint32_t i = 0; i < vertexShader->samplerVarCount; i++) {
writeString(data, vertexShader->samplerVars[i].name);
write(data, vertexShader->samplerVars[i].type);
write(data, vertexShader->samplerVars[i].location);
}
// write attribute vars
write(data, vertexShader->attribVarCount);
for (uint32_t i = 0; i < vertexShader->attribVarCount; i++) {
writeString(data, vertexShader->attribVars[i].name);
write(data, vertexShader->attribVars[i].type);
write(data, vertexShader->attribVars[i].count);
write(data, vertexShader->attribVars[i].location);
}
// write ring item size
write(data, vertexShader->ringItemsize);
// write stream out
write(data, vertexShader->hasStreamOut);
for (uint32_t stride : vertexShader->streamOutStride) {
write(data, stride);
}
// write gx2rBuffer
writeGX2RBuffer(data, &vertexShader->gx2rBuffer);
return data;
}
std::vector<uint8_t> SerializePixelShader(GX2PixelShader *pixelShader) {
std::vector<uint8_t> data;
// write regs
write(data, pixelShader->regs.sq_pgm_resources_ps);
write(data, pixelShader->regs.sq_pgm_exports_ps);
write(data, pixelShader->regs.spi_ps_in_control_0);
write(data, pixelShader->regs.spi_ps_in_control_1);
write(data, pixelShader->regs.num_spi_ps_input_cntl);
for (uint32_t spi_ps : pixelShader->regs.spi_ps_input_cntls) {
write(data, spi_ps);
}
write(data, pixelShader->regs.cb_shader_mask);
write(data, pixelShader->regs.cb_shader_control);
write(data, pixelShader->regs.db_shader_control);
write(data, pixelShader->regs.spi_input_z);
// write program
write(data, pixelShader->size);
for (uint32_t i = 0; i < pixelShader->size; i++) {
write(data, ((uint8_t *) pixelShader->program)[i]);
}
write(data, pixelShader->mode);
// write uniform blocks
write(data, pixelShader->uniformBlockCount);
for (uint32_t i = 0; i < pixelShader->uniformBlockCount; i++) {
writeString(data, pixelShader->uniformBlocks[i].name);
write(data, pixelShader->uniformBlocks[i].offset);
write(data, pixelShader->uniformBlocks[i].size);
}
// write uniform vars
write(data, pixelShader->uniformVarCount);
for (uint32_t i = 0; i < pixelShader->uniformVarCount; i++) {
writeString(data, pixelShader->uniformVars[i].name);
write(data, pixelShader->uniformVars[i].type);
write(data, pixelShader->uniformVars[i].count);
write(data, pixelShader->uniformVars[i].offset);
write(data, pixelShader->uniformVars[i].block);
}
// write initial values
write(data, pixelShader->initialValueCount);
for (uint32_t i = 0; i < pixelShader->initialValueCount; i++) {
write(data, pixelShader->initialValues[i].value[0]);
write(data, pixelShader->initialValues[i].value[1]);
write(data, pixelShader->initialValues[i].value[2]);
write(data, pixelShader->initialValues[i].value[3]);
write(data, pixelShader->initialValues[i].offset);
}
// write loop vars
write(data, pixelShader->loopVarCount);
for (uint32_t i = 0; i < pixelShader->loopVarCount; i++) {
write(data, pixelShader->loopVars[i].offset);
write(data, pixelShader->loopVars[i].value);
}
// write sampler vars
write(data, pixelShader->samplerVarCount);
for (uint32_t i = 0; i < pixelShader->samplerVarCount; i++) {
writeString(data, pixelShader->samplerVars[i].name);
write(data, pixelShader->samplerVars[i].type);
write(data, pixelShader->samplerVars[i].location);
}
// write gx2rBuffer
writeGX2RBuffer(data, &pixelShader->gx2rBuffer);
return data;
}
template<typename Type>
static Type readAt(const std::span<const uint8_t> &data, size_t &pos) {
Type value = *reinterpret_cast<const Type *>(data.data() + pos);
pos += sizeof(Type);
return value;
}
static const char *readString(const std::span<const uint8_t> &data, size_t &pos) {
std::string str(reinterpret_cast<const char *>(data.data() + pos));
pos += str.size() + 1;
pos = (pos + 3) & ~3; // align to 4 bytes
// Allocate memory for the string and copy the contents
char *result = static_cast<char *>(malloc(sizeof(char) * (str.size() + 1)));
strcpy(result, str.c_str());
return result;
}
static GX2RBuffer readGX2RBuffer(const std::span<const uint8_t> &data, size_t &pos) {
GX2RBuffer buffer;
buffer.flags = readAt<GX2RResourceFlags>(data, pos);
buffer.elemSize = readAt<uint32_t>(data, pos);
buffer.elemCount = readAt<uint32_t>(data, pos);
size_t bufferSize = buffer.elemSize * buffer.elemCount;
buffer.buffer = malloc(sizeof(uint8_t) * bufferSize);
memcpy(buffer.buffer, data.data() + pos, bufferSize);
pos += bufferSize;
return buffer;
}
std::unique_ptr<GX2VertexShaderWrapper> DeserializeVertexShader(const std::span<const uint8_t> &data) {
size_t pos = 0;
auto vertexShaderWrapper = std::make_unique<GX2VertexShaderWrapper>();
auto *vertexShader = vertexShaderWrapper->getVertexShader();
*vertexShader = {};
// read regs
vertexShader->regs.sq_pgm_resources_vs = readAt<uint32_t>(data, pos);
vertexShader->regs.vgt_primitiveid_en = readAt<uint32_t>(data, pos);
vertexShader->regs.spi_vs_out_config = readAt<uint32_t>(data, pos);
vertexShader->regs.num_spi_vs_out_id = readAt<uint32_t>(data, pos);
for (uint32_t &spi_vs : vertexShader->regs.spi_vs_out_id) {
spi_vs = readAt<uint32_t>(data, pos);
}
vertexShader->regs.pa_cl_vs_out_cntl = readAt<uint32_t>(data, pos);
vertexShader->regs.sq_vtx_semantic_clear = readAt<uint32_t>(data, pos);
vertexShader->regs.num_sq_vtx_semantic = readAt<uint32_t>(data, pos);
for (uint32_t &sq_vtx : vertexShader->regs.sq_vtx_semantic) {
sq_vtx = readAt<uint32_t>(data, pos);
}
vertexShader->regs.vgt_strmout_buffer_en = readAt<uint32_t>(data, pos);
vertexShader->regs.vgt_vertex_reuse_block_cntl = readAt<uint32_t>(data, pos);
vertexShader->regs.vgt_hos_reuse_depth = readAt<uint32_t>(data, pos);
// read program
vertexShader->size = readAt<uint32_t>(data, pos);
vertexShader->program = memalign(256, vertexShader->size);
for (uint32_t i = 0; i < vertexShader->size; i++) {
static_cast<uint8_t *>(vertexShader->program)[i] = readAt<uint8_t>(data, pos);
}
vertexShader->mode = readAt<GX2ShaderMode>(data, pos);
// read uniform blocks
vertexShader->uniformBlockCount = readAt<uint32_t>(data, pos);
if (vertexShader->uniformBlockCount > 0) {
vertexShader->uniformBlocks = static_cast<GX2UniformBlock *>(malloc(sizeof(GX2UniformBlock) * vertexShader->uniformBlockCount));
for (uint32_t i = 0; i < vertexShader->uniformBlockCount; i++) {
vertexShader->uniformBlocks[i].name = readString(data, pos);
vertexShader->uniformBlocks[i].offset = readAt<uint32_t>(data, pos);
vertexShader->uniformBlocks[i].size = readAt<uint32_t>(data, pos);
}
}
// read uniform vars
vertexShader->uniformVarCount = readAt<uint32_t>(data, pos);
if (vertexShader->uniformVarCount > 0) {
vertexShader->uniformVars = static_cast<GX2UniformVar *>(malloc(sizeof(GX2UniformVar) * vertexShader->uniformVarCount));
for (uint32_t i = 0; i < vertexShader->uniformVarCount; i++) {
vertexShader->uniformVars[i].name = readString(data, pos);
vertexShader->uniformVars[i].type = readAt<GX2ShaderVarType>(data, pos);
vertexShader->uniformVars[i].count = readAt<uint32_t>(data, pos);
vertexShader->uniformVars[i].offset = readAt<uint32_t>(data, pos);
vertexShader->uniformVars[i].block = readAt<int32_t>(data, pos);
}
}
// read initial values
vertexShader->initialValueCount = readAt<uint32_t>(data, pos);
if (vertexShader->initialValueCount > 0) {
vertexShader->initialValues = static_cast<GX2UniformInitialValue *>(malloc(sizeof(GX2UniformInitialValue) * vertexShader->initialValueCount));
for (uint32_t i = 0; i < vertexShader->initialValueCount; i++) {
vertexShader->initialValues[i].value[0] = readAt<float>(data, pos);
vertexShader->initialValues[i].value[1] = readAt<float>(data, pos);
vertexShader->initialValues[i].value[2] = readAt<float>(data, pos);
vertexShader->initialValues[i].value[3] = readAt<float>(data, pos);
vertexShader->initialValues[i].offset = readAt<uint32_t>(data, pos);
}
}
// read loop vars
vertexShader->loopVarCount = readAt<uint32_t>(data, pos);
if (vertexShader->loopVarCount > 0) {
vertexShader->loopVars = static_cast<GX2LoopVar *>(malloc(sizeof(GX2LoopVar) + vertexShader->loopVarCount));
for (uint32_t i = 0; i < vertexShader->loopVarCount; i++) {
vertexShader->loopVars[i].offset = readAt<uint32_t>(data, pos);
vertexShader->loopVars[i].value = readAt<uint32_t>(data, pos);
}
}
// read sampler vars
vertexShader->samplerVarCount = readAt<uint32_t>(data, pos);
if (vertexShader->samplerVarCount > 0) {
vertexShader->samplerVars = static_cast<GX2SamplerVar *>(malloc(sizeof(GX2SamplerVar) * vertexShader->samplerVarCount));
for (uint32_t i = 0; i < vertexShader->samplerVarCount; i++) {
vertexShader->samplerVars[i].name = readString(data, pos);
vertexShader->samplerVars[i].type = readAt<GX2SamplerVarType>(data, pos);
vertexShader->samplerVars[i].location = readAt<uint32_t>(data, pos);
}
}
// read attribute vars
vertexShader->attribVarCount = readAt<uint32_t>(data, pos);
if (vertexShader->attribVarCount > 0) {
vertexShader->attribVars = static_cast<GX2AttribVar *>(malloc(sizeof(GX2AttribVar) * vertexShader->attribVarCount));
for (uint32_t i = 0; i < vertexShader->attribVarCount; i++) {
vertexShader->attribVars[i].name = readString(data, pos);
vertexShader->attribVars[i].type = readAt<GX2ShaderVarType>(data, pos);
vertexShader->attribVars[i].count = readAt<uint32_t>(data, pos);
vertexShader->attribVars[i].location = readAt<uint32_t>(data, pos);
}
}
// read ring item size
vertexShader->ringItemsize = readAt<uint32_t>(data, pos);
// read stream out
vertexShader->hasStreamOut = readAt<BOOL>(data, pos);
for (uint32_t &stride : vertexShader->streamOutStride) {
stride = readAt<uint32_t>(data, pos);
}
// read gx2rBuffer
vertexShader->gx2rBuffer = readGX2RBuffer(data, pos);
return vertexShaderWrapper;
}
std::unique_ptr<GX2PixelShaderWrapper> DeserializePixelShader(const std::span<const uint8_t> &data) {
size_t pos = 0;
auto pixelShaderWrapper = std::make_unique<GX2PixelShaderWrapper>();
auto *pixelShader = pixelShaderWrapper->getPixelShader();
*pixelShader = {};
// read regs
pixelShader->regs.sq_pgm_resources_ps = readAt<uint32_t>(data, pos);
pixelShader->regs.sq_pgm_exports_ps = readAt<uint32_t>(data, pos);
pixelShader->regs.spi_ps_in_control_0 = readAt<uint32_t>(data, pos);
pixelShader->regs.spi_ps_in_control_1 = readAt<uint32_t>(data, pos);
pixelShader->regs.num_spi_ps_input_cntl = readAt<uint32_t>(data, pos);
for (uint32_t &spi_ps : pixelShader->regs.spi_ps_input_cntls) {
spi_ps = readAt<uint32_t>(data, pos);
}
pixelShader->regs.cb_shader_mask = readAt<uint32_t>(data, pos);
pixelShader->regs.cb_shader_control = readAt<uint32_t>(data, pos);
pixelShader->regs.db_shader_control = readAt<uint32_t>(data, pos);
pixelShader->regs.spi_input_z = readAt<uint32_t>(data, pos);
// read program
pixelShader->size = readAt<uint32_t>(data, pos);
pixelShader->program = memalign(256, pixelShader->size);
for (uint32_t i = 0; i < pixelShader->size; i++) {
((uint8_t *) pixelShader->program)[i] = readAt<uint8_t>(data, pos);
}
pixelShader->mode = readAt<GX2ShaderMode>(data, pos);
// read uniform blocks
pixelShader->uniformBlockCount = readAt<uint32_t>(data, pos);
if (pixelShader->uniformBlockCount > 0) {
pixelShader->uniformBlocks = static_cast<GX2UniformBlock *>(malloc(sizeof(GX2UniformBlock) * pixelShader->uniformBlockCount));
for (uint32_t i = 0; i < pixelShader->uniformBlockCount; i++) {
pixelShader->uniformBlocks[i].name = readString(data, pos);
pixelShader->uniformBlocks[i].offset = readAt<uint32_t>(data, pos);
pixelShader->uniformBlocks[i].size = readAt<uint32_t>(data, pos);
}
}
// read uniform vars
pixelShader->uniformVarCount = readAt<uint32_t>(data, pos);
if (pixelShader->uniformVarCount > 0) {
pixelShader->uniformVars = static_cast<GX2UniformVar *>(malloc(sizeof(GX2UniformVar) + pixelShader->uniformVarCount));
for (uint32_t i = 0; i < pixelShader->uniformVarCount; i++) {
pixelShader->uniformVars[i].name = readString(data, pos);
pixelShader->uniformVars[i].type = readAt<GX2ShaderVarType>(data, pos);
pixelShader->uniformVars[i].count = readAt<uint32_t>(data, pos);
pixelShader->uniformVars[i].offset = readAt<uint32_t>(data, pos);
pixelShader->uniformVars[i].block = readAt<int32_t>(data, pos);
}
}
// read initial values
pixelShader->initialValueCount = readAt<uint32_t>(data, pos);
if (pixelShader->initialValueCount > 0) {
pixelShader->initialValues = static_cast<GX2UniformInitialValue *>(malloc(sizeof(GX2UniformInitialValue) * pixelShader->initialValueCount));
for (uint32_t i = 0; i < pixelShader->initialValueCount; i++) {
pixelShader->initialValues[i].value[0] = readAt<float>(data, pos);
pixelShader->initialValues[i].value[1] = readAt<float>(data, pos);
pixelShader->initialValues[i].value[2] = readAt<float>(data, pos);
pixelShader->initialValues[i].value[3] = readAt<float>(data, pos);
pixelShader->initialValues[i].offset = readAt<uint32_t>(data, pos);
}
}
pixelShader->loopVarCount = readAt<uint32_t>(data, pos);
if (pixelShader->loopVarCount > 0) {
pixelShader->loopVars = static_cast<GX2LoopVar *>(malloc(sizeof(GX2LoopVar) * pixelShader->loopVarCount));
for (uint32_t i = 0; i < pixelShader->loopVarCount; i++) {
pixelShader->loopVars[i].offset = readAt<uint32_t>(data, pos);
pixelShader->loopVars[i].value = readAt<uint32_t>(data, pos);
}
}
pixelShader->samplerVarCount = readAt<uint32_t>(data, pos);
if (pixelShader->samplerVarCount > 0) {
pixelShader->samplerVars = static_cast<GX2SamplerVar *>(malloc(sizeof(GX2SamplerVar) * pixelShader->samplerVarCount));
for (uint32_t i = 0; i < pixelShader->samplerVarCount; i++) {
pixelShader->samplerVars[i].name = readString(data, pos);
pixelShader->samplerVars[i].type = readAt<GX2SamplerVarType>(data, pos);
pixelShader->samplerVars[i].location = readAt<uint32_t>(data, pos);
}
}
pixelShader->gx2rBuffer = readGX2RBuffer(data, pos);
return pixelShaderWrapper;
}

View File

@ -0,0 +1,120 @@
#pragma once
#include <gx2/shaders.h>
#include <memory>
#include <span>
#include <vector>
class GX2PixelShaderWrapper {
public:
[[nodiscard]] GX2PixelShader *getPixelShader() {
return &pixelShader;
}
~GX2PixelShaderWrapper() {
if (pixelShader.program) {
free(pixelShader.program);
}
if (pixelShader.uniformBlocks) {
for (uint32_t i = 0; i < pixelShader.uniformBlockCount; i++) {
free((void *) pixelShader.uniformBlocks[i].name);
}
free(pixelShader.uniformBlocks);
}
if (pixelShader.uniformVars) {
for (uint32_t i = 0; i < pixelShader.uniformVarCount; i++) {
free((void *) pixelShader.uniformVars[i].name);
}
free(pixelShader.uniformVars);
}
if (pixelShader.initialValues) {
free(pixelShader.initialValues);
}
if (pixelShader.samplerVars) {
for (uint32_t i = 0; i < pixelShader.samplerVarCount; i++) {
free((void *) pixelShader.samplerVars[i].name);
}
free(pixelShader.samplerVars);
}
if (pixelShader.loopVars) {
free(pixelShader.loopVars);
}
if (pixelShader.gx2rBuffer.buffer) {
free(pixelShader.gx2rBuffer.buffer);
}
}
private:
alignas(0x40) GX2PixelShader pixelShader;
};
class GX2VertexShaderWrapper {
public:
[[nodiscard]] GX2VertexShader *getVertexShader() {
return &vertexShader;
}
~GX2VertexShaderWrapper() {
if (vertexShader.program) {
free(vertexShader.program);
}
if (vertexShader.uniformBlocks) {
for (uint32_t i = 0; i < vertexShader.uniformBlockCount; i++) {
free((void *) vertexShader.uniformBlocks[i].name);
}
free(vertexShader.uniformBlocks);
}
if (vertexShader.uniformVars) {
for (uint32_t i = 0; i < vertexShader.uniformVarCount; i++) {
free((void *) vertexShader.uniformVars[i].name);
}
free(vertexShader.uniformVars);
}
if (vertexShader.initialValues) {
free(vertexShader.initialValues);
}
if (vertexShader.loopVars) {
free(vertexShader.loopVars);
}
if (vertexShader.samplerVars) {
for (uint32_t i = 0; i < vertexShader.samplerVarCount; i++) {
free((void *) vertexShader.samplerVars[i].name);
}
free(vertexShader.samplerVars);
}
if (vertexShader.attribVars) {
for (uint32_t i = 0; i < vertexShader.attribVarCount; i++) {
free((void *) vertexShader.attribVars[i].name);
}
free(vertexShader.attribVars);
}
}
private:
alignas(0x40) GX2VertexShader vertexShader;
};
std::vector<uint8_t> SerializeVertexShader(GX2VertexShader *vertexShader);
std::vector<uint8_t> SerializePixelShader(GX2PixelShader *pixelShader);
std::unique_ptr<GX2VertexShaderWrapper> DeserializeVertexShader(const std::span<const uint8_t> &data);
std::unique_ptr<GX2PixelShaderWrapper> DeserializePixelShader(const std::span<const uint8_t> &data);

View File

@ -0,0 +1,220 @@
#include "SplashScreenDrawer.h"
#include "ShaderSerializer.h"
#include "TGATexture.h"
#include "gfx.h"
#include "logger.h"
#include "utils.h"
#include <fstream>
#include <gx2/draw.h>
#include <gx2/mem.h>
#include <gx2r/draw.h>
#include <whb/log.h>
/*
constexpr const char *s_textureVertexShader = R"(
#version 450
layout(location = 0) in vec2 aPos;
layout(location = 1) in vec2 aTexCoord;
layout(location = 0) out vec2 TexCoord;
void main()
{
TexCoord = aTexCoord;
gl_Position = vec4(aPos.x, aPos.y, 0.0f, 1.0f);
}
)";
*/
constexpr uint8_t s_textureVertexShaderCompiled[] = {
0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x8A, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x80, 0x89, 0x00, 0x40, 0x01, 0xC0, 0xC8, 0x0F, 0x00, 0x94,
0x3C, 0xA0, 0x00, 0xC0, 0x08, 0x0B, 0x00, 0x94, 0x05, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x80,
0x00, 0x00, 0x00, 0x80, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0F, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x61, 0x50, 0x6F, 0x73,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x61, 0x54, 0x65, 0x78, 0x43, 0x6F, 0x6F, 0x72,
0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00};
/*
constexpr const char *s_texturePixelShader = R"(
#version 450
#extension GL_ARB_shading_language_420pack: enable
layout(location = 0) in vec2 TexCoord;
layout(location = 0) out vec4 FragColor;
layout(binding = 0) uniform sampler2D inTexture;
void main()
{
FragColor = texture(inTexture, TexCoord);
}
)";*/
constexpr uint8_t s_texturePixelShaderCompiled[] = {
0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x8A,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0xC0,
0x88, 0x06, 0x20, 0x94, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0D, 0xF0,
0x00, 0x00, 0x80, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0F, 0xFF,
0x00, 0x00, 0x00, 0x01, 0x69, 0x6E, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72,
0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static GX2Texture *LoadTGAAsTexture(std::string_view path) {
std::vector<uint8_t> buffer;
if (!LoadFileIntoBuffer(path, buffer)) {
return nullptr;
}
auto *texture = TGA_LoadTexture(buffer);
if (texture == nullptr) {
return nullptr;
}
return texture;
}
SplashScreenDrawer::SplashScreenDrawer(std::string_view meta_dir) {
// create shader group
mVertexShaderWrapper = DeserializeVertexShader(s_textureVertexShaderCompiled);
mPixelShaderWrapper = DeserializePixelShader(s_texturePixelShaderCompiled);
mShaderGroup = {};
mShaderGroup.vertexShader = mVertexShaderWrapper->getVertexShader();
mShaderGroup.pixelShader = mPixelShaderWrapper->getPixelShader();
GX2Invalidate(GX2_INVALIDATE_MODE_CPU_SHADER, mShaderGroup.vertexShader->program, mShaderGroup.vertexShader->size);
GX2Invalidate(GX2_INVALIDATE_MODE_CPU_SHADER, mShaderGroup.pixelShader->program, mShaderGroup.pixelShader->size);
GX2SetShaderMode(GX2_SHADER_MODE_UNIFORM_BLOCK);
GfxInitShaderAttribute(&mShaderGroup, "aPos", 0, 0, GX2_ATTRIB_FORMAT_FLOAT_32_32);
GfxInitShaderAttribute(&mShaderGroup, "aTexCoord", 1, 0, GX2_ATTRIB_FORMAT_FLOAT_32_32);
GfxInitFetchShader(&mShaderGroup);
// upload vertex position
mPositionBuffer.flags = GX2R_RESOURCE_BIND_VERTEX_BUFFER | GX2R_RESOURCE_USAGE_CPU_READ | GX2R_RESOURCE_USAGE_CPU_WRITE | GX2R_RESOURCE_USAGE_GPU_READ;
mPositionBuffer.elemSize = 2 * sizeof(float);
mPositionBuffer.elemCount = 4;
GX2RCreateBuffer(&mPositionBuffer);
void *posUploadBuffer = GX2RLockBufferEx(&mPositionBuffer, GX2R_RESOURCE_BIND_NONE);
memcpy(posUploadBuffer, mPositionData, mPositionBuffer.elemSize * mPositionBuffer.elemCount);
GX2RUnlockBufferEx(&mPositionBuffer, GX2R_RESOURCE_BIND_NONE);
// upload texture coords
mTexCoordBuffer.flags = GX2R_RESOURCE_BIND_VERTEX_BUFFER | GX2R_RESOURCE_USAGE_CPU_READ | GX2R_RESOURCE_USAGE_CPU_WRITE | GX2R_RESOURCE_USAGE_GPU_READ;
mTexCoordBuffer.elemSize = 2 * sizeof(float);
mTexCoordBuffer.elemCount = 4;
GX2RCreateBuffer(&mTexCoordBuffer);
void *coordsUploadBuffer = GX2RLockBufferEx(&mTexCoordBuffer, GX2R_RESOURCE_BIND_NONE);
memcpy(coordsUploadBuffer, mTexCoords, mTexCoordBuffer.elemSize * mTexCoordBuffer.elemCount);
GX2RUnlockBufferEx(&mTexCoordBuffer, GX2R_RESOURCE_BIND_NONE);
std::string bootTvTex = std::string(meta_dir).append("/bootTvTex.tga");
std::string bootDrcTex = std::string(meta_dir).append("/bootDrcTex.tga");
mTextureTV = LoadTGAAsTexture(bootTvTex);
mTextureDRC = LoadTGAAsTexture(bootDrcTex);
GX2Sampler sampler;
GX2InitSampler(&sampler, GX2_TEX_CLAMP_MODE_CLAMP, GX2_TEX_XY_FILTER_MODE_LINEAR);
}
void SplashScreenDrawer::Draw() {
if (!mTextureTV || !mTextureDRC) {
DEBUG_FUNCTION_LINE_ERR("Textures are missing");
return;
}
GfxBeginRender();
GfxBeginRenderTV();
GX2SetFetchShader(&mShaderGroup.fetchShader);
GX2SetVertexShader(mShaderGroup.vertexShader);
GX2SetPixelShader(mShaderGroup.pixelShader);
GX2SetShaderMode(GX2_SHADER_MODE_UNIFORM_BLOCK);
GX2RSetAttributeBuffer(&mPositionBuffer, 0, mPositionBuffer.elemSize, 0);
GX2RSetAttributeBuffer(&mTexCoordBuffer, 1, mTexCoordBuffer.elemSize, 0);
GX2SetPixelTexture(mTextureTV, mShaderGroup.pixelShader->samplerVars[0].location);
GX2SetPixelSampler(&mSampler, mShaderGroup.pixelShader->samplerVars[0].location);
GX2DrawEx(GX2_PRIMITIVE_MODE_QUADS, 4, 0, 1);
GfxFinishRenderTV();
GfxBeginRenderDRC();
GX2SetFetchShader(&mShaderGroup.fetchShader);
GX2SetVertexShader(mShaderGroup.vertexShader);
GX2SetPixelShader(mShaderGroup.pixelShader);
GX2SetShaderMode(GX2_SHADER_MODE_UNIFORM_BLOCK);
GX2RSetAttributeBuffer(&mPositionBuffer, 0, mPositionBuffer.elemSize, 0);
GX2RSetAttributeBuffer(&mTexCoordBuffer, 1, mTexCoordBuffer.elemSize, 0);
GX2SetPixelTexture(mTextureDRC, mShaderGroup.pixelShader->samplerVars[0].location);
GX2SetPixelSampler(&mSampler, mShaderGroup.pixelShader->samplerVars[0].location);
GX2DrawEx(GX2_PRIMITIVE_MODE_QUADS, 4, 0, 1);
GfxFinishRenderDRC();
GfxFinishRender();
}
SplashScreenDrawer::~SplashScreenDrawer() {
GX2RDestroyBufferEx(&mPositionBuffer, GX2R_RESOURCE_BIND_NONE);
GX2RDestroyBufferEx(&mTexCoordBuffer, GX2R_RESOURCE_BIND_NONE);
if (mTextureTV) {
if (mTextureTV->surface.image != nullptr) {
free(mTextureTV->surface.image);
mTextureTV->surface.image = nullptr;
}
::free(mTextureTV);
mTextureTV = nullptr;
}
if (mTextureDRC) {
if (mTextureDRC->surface.image != nullptr) {
free(mTextureDRC->surface.image);
mTextureDRC->surface.image = nullptr;
}
::free(mTextureDRC);
mTextureDRC = nullptr;
}
}

View File

@ -0,0 +1,50 @@
#pragma once
#include "ShaderSerializer.h"
#include "gfx.h"
#include <gx2/sampler.h>
#include <gx2/shaders.h>
#include <gx2/texture.h>
#include <gx2r/buffer.h>
#include <memory>
class SplashScreenDrawer {
public:
explicit SplashScreenDrawer(std::string_view meta_dir);
void Draw();
virtual ~SplashScreenDrawer();
private:
const float mPositionData[8] = {
-1.0f,
-1.0f,
1.0f,
-1.0f,
1.0f,
1.0f,
-1.0f,
1.0f,
};
const float mTexCoords[8] = {
0.0f,
1.0f,
1.0f,
1.0f,
1.0f,
0.0f,
0.0f,
0.0f,
};
WHBGfxShaderGroup mShaderGroup = {};
std::unique_ptr<GX2VertexShaderWrapper> mVertexShaderWrapper;
std::unique_ptr<GX2PixelShaderWrapper> mPixelShaderWrapper;
GX2RBuffer mPositionBuffer = {};
GX2RBuffer mTexCoordBuffer = {};
GX2Texture *mTextureTV = nullptr;
GX2Texture *mTextureDRC = nullptr;
GX2Sampler mSampler = {};
};

View File

@ -0,0 +1,102 @@
#include "SplashSoundPlayer.h"
#include "logger.h"
#include "utils.h"
#include <coreinit/transition.h>
#include <cstring>
#include <sndcore2/core.h>
#include <sndcore2/device.h>
#include <span>
#include <string>
#include <sys/stat.h>
#include <vector>
#include <whb/log.h>
SplashSoundPlayer::SplashSoundPlayer(std::string_view meta_dir) {
std::string bootSound = std::string(meta_dir).append("/bootSound.btsnd");
if (!LoadFileIntoBuffer(bootSound, mBuffer)) {
mBuffer.clear();
return;
}
if (mBuffer.size() < 8) {
return;
}
auto target = ((uint32_t *) mBuffer.data())[0];
if (target <= 2) {
mOutputTarget = static_cast<TransitionAudioTarget>(target);
}
mLoopPoint = ((uint32_t *) mBuffer.data())[1];
}
void SplashSoundPlayer::Play() {
if (mBuffer.empty() || mBuffer.size() < 8) {
DEBUG_FUNCTION_LINE_WARN("mBuffed is empty or too small");
return;
}
AXTransitionAudioBuffer transitionAudioBuffer = {};
AXInit();
AXDeviceMode tvMode;
AXDeviceMode drcMode;
AXGetDeviceMode(0, &tvMode);
AXGetDeviceMode(1, &drcMode);
AXQuit();
WHBLogPrintf("TV mode before transition = %d\n", tvMode);
transitionAudioBuffer.tv.mode = tvMode;
WHBLogPrintf("DRC mode before transition = %d\n", drcMode);
transitionAudioBuffer.drc.mode = drcMode;
transitionAudioBuffer.unk1 = 1;
transitionAudioBuffer.unk2 = 0;
transitionAudioBuffer.tv.unk1 = 0.99987207655f;
transitionAudioBuffer.tv.unk2 = 600;
transitionAudioBuffer.drc.unk1 = transitionAudioBuffer.tv.unk1;
transitionAudioBuffer.tv.unk2 = transitionAudioBuffer.tv.unk2;
switch (mOutputTarget) {
case TV_ONLY:
transitionAudioBuffer.tv.enabled = true;
transitionAudioBuffer.drc.enabled = false;
break;
case DRC_ONLY:
transitionAudioBuffer.tv.enabled = false;
transitionAudioBuffer.drc.enabled = true;
break;
case BOTH:
transitionAudioBuffer.tv.enabled = true;
transitionAudioBuffer.drc.enabled = true;
break;
}
void *audioBuffer = (void *) nullptr;
uint32_t audioBufferLen = 0;
auto res = __OSGetTransitionAudioBuffer(&audioBuffer, &audioBufferLen);
if (res == 0) {
DEBUG_FUNCTION_LINE_ERR("Could not get access to audio buffer from foreground bucket\n");
return;
}
std::span<uint8_t> audioBufferIn(mBuffer.data() + 8, mBuffer.size() - 8);
DEBUG_FUNCTION_LINE("Got audio buffer from foreground bucket @ %8.8x len = %d\n", audioBuffer, audioBufferLen);
if (audioBufferLen < mBuffer.size()) {
DEBUG_FUNCTION_LINE_ERR("buffer not big enough");
return;
}
memcpy(audioBuffer, audioBufferIn.data(), audioBufferIn.size());
__OSSetTransitionAudioSize(audioBufferIn.size());
transitionAudioBuffer.length = audioBufferIn.size();
transitionAudioBuffer.loopPoint = mLoopPoint;
transitionAudioBuffer.audioBuffer = audioBuffer;
transitionAudioBuffer.audioBufferLen = audioBufferLen;
AXSetUpTransitionAudio((AXTransitionAudioBuffer *) &transitionAudioBuffer);
AXStartTransitionAudio();
}

View File

@ -0,0 +1,23 @@
#pragma once
#include <cstdint>
#include <string_view>
#include <vector>
class SplashSoundPlayer {
public:
SplashSoundPlayer(std::string_view meta_dir);
void Play();
virtual ~SplashSoundPlayer() = default;
private:
enum TransitionAudioTarget {
TV_ONLY,
DRC_ONLY,
BOTH
};
std::vector<uint8_t> mBuffer;
TransitionAudioTarget mOutputTarget = BOTH;
uint32_t mLoopPoint = 0;
};

View File

@ -0,0 +1,84 @@
#include <gx2/draw.h>
#include <gx2/mem.h>
#include <malloc.h>
#include <span>
#include "TGATexture.h"
#include "logger.h"
#include <whb/log.h>
/*
* Based on
* https://github.com/Xpl0itU/savemii/blob/70e3b63db52113519230e1e39bd56876cef12dc8/src/tga_reader.cpp
* and
* https://github.com/Crementif/WiiU-GX2-Shader-Examples/blob/5a88f861043dcb7666d4d25a6bab6bd271e76d5f/include/TGATexture.h
*/
uint16_t inline _swapU16(uint16_t v) {
return (v >> 8) | (v << 8);
}
GX2Texture *TGA_LoadTexture(std::span<uint8_t> data) {
TGA_HEADER *tgaHeader = (TGA_HEADER *) data.data();
uint32_t width = _swapU16(tgaHeader->width);
uint32_t height = _swapU16(tgaHeader->height);
if (tgaHeader->bits != 24) {
DEBUG_FUNCTION_LINE_WARN("Only 24bit TGA images are supported");
return nullptr;
}
if (tgaHeader->imagetype != 2 && tgaHeader->imagetype != 3) {
DEBUG_FUNCTION_LINE_WARN("Only uncompressed TGA images are supported");
return nullptr;
}
GX2Texture *texture = (GX2Texture *) malloc(sizeof(GX2Texture));
*texture = {};
texture->surface.width = width;
texture->surface.height = height;
texture->surface.depth = 1;
texture->surface.mipLevels = 1;
texture->surface.format = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8;
texture->surface.aa = GX2_AA_MODE1X;
texture->surface.use = GX2_SURFACE_USE_TEXTURE;
texture->surface.dim = GX2_SURFACE_DIM_TEXTURE_2D;
texture->surface.tileMode = GX2_TILE_MODE_LINEAR_ALIGNED;
texture->surface.swizzle = 0;
texture->viewFirstMip = 0;
texture->viewNumMips = 1;
texture->viewFirstSlice = 0;
texture->viewNumSlices = 1;
texture->compMap = 0x0010203;
GX2CalcSurfaceSizeAndAlignment(&texture->surface);
GX2InitTextureRegs(texture);
if (texture->surface.imageSize == 0) {
return nullptr;
}
texture->surface.image = memalign(texture->surface.alignment, texture->surface.imageSize);
if (!texture->surface.image) {
return nullptr;
}
for (uint32_t y = 0; y < height; y++) {
uint32_t *out_data = (uint32_t *) texture->surface.image + (y * texture->surface.pitch);
for (uint32_t x = 0; x < width; x++) {
int index = sizeof(TGA_HEADER) + (3 * width * (height - 1 - y)) + (3 * x);
int b = data[index + 0] & 0xFF;
int g = data[index + 1] & 0xFF;
int r = data[index + 2] & 0xFF;
*out_data = r << 24 | g << 16 | b << 8 | 0xFF;
out_data++;
}
}
// todo: create texture with optimal tile format and use GX2CopySurface to convert from linear to tiled format
GX2Invalidate(GX2_INVALIDATE_MODE_CPU | GX2_INVALIDATE_MODE_TEXTURE, texture->surface.image, texture->surface.imageSize);
return texture;
}

24
source/utils/TGATexture.h Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include <cstdint>
#include <gx2/texture.h>
struct WUT_PACKED TGA_HEADER {
uint8_t identsize; // size of ID field that follows 18 byte header (0 usually)
uint8_t colourmaptype; // type of colour map 0=none, 1=has palette
uint8_t imagetype; // type of image 0=none,1=indexed,2=rgb,3=grey,+8=rle packed
uint8_t colourmapstart[2]; // first colour map entry in palette
uint8_t colourmaplength[2]; // number of colours in palette
uint8_t colourmapbits; // number of bits per palette entry 15,16,24,32
uint16_t xstart; // image x origin
uint16_t ystart; // image y origin
uint16_t width; // image width in pixels
uint16_t height; // image height in pixels
uint8_t bits; // image bits per pixel 8,16,24,32
uint8_t descriptor; // image descriptor bits (vh flip bits)
};
// quick and dirty 24-bit TGA loader
GX2Texture *TGA_LoadTexture(std::span<uint8_t> data);

549
source/utils/gfx.c Normal file
View File

@ -0,0 +1,549 @@
/*
* Based on https://github.com/devkitPro/wut/blob/4933211d7ba86d4dd45c8525fc83747d799ecf31/libraries/libwhb/src/gfx.c
*/
#include "logger.h"
#include <avm/drc.h>
#include <avm/tv.h>
#include <coreinit/debug.h>
#include <coreinit/memdefaultheap.h>
#include <coreinit/memexpheap.h>
#include <coreinit/memfrmheap.h>
#include <coreinit/memheap.h>
#include <coreinit/savedframe.h>
#include <gx2/clear.h>
#include <gx2/context.h>
#include <gx2/display.h>
#include <gx2/event.h>
#include <gx2/mem.h>
#include <gx2/registers.h>
#include <gx2/shaders.h>
#include <gx2/state.h>
#include <gx2/surface.h>
#include <gx2/swap.h>
#include <gx2/utils.h>
#include <gx2r/buffer.h>
#include <gx2r/mem.h>
#include <proc_ui/procui.h>
#include <string.h>
#include <whb/gfx.h>
#include <whb/log.h>
#define WHB_GFX_COMMAND_BUFFER_POOL_SIZE (0x400000)
static void *sCommandBufferPool = NULL;
static GX2DrcRenderMode sDrcRenderMode;
static void *sDrcScanBuffer = NULL;
static uint32_t sDrcScanBufferSize = 0;
static GX2SurfaceFormat sDrcSurfaceFormat;
static GX2TVRenderMode sTvRenderMode;
static void *sTvScanBuffer = NULL;
static uint32_t sTvScanBufferSize = 0;
static GX2SurfaceFormat sTvSurfaceFormat;
static GX2SurfaceFormat sDrcSurfaceFormat;
static GX2ColorBuffer sTvColourBuffer = {0};
static GX2ColorBuffer sDrcColourBuffer = {0};
static GX2ContextState *sTvContextState = NULL;
static GX2ContextState *sDrcContextState = NULL;
static BOOL sGpuTimedOut = FALSE;
static void *sGfxHeapForeground = NULL;
static void *AllocMEM2(uint32_t size, uint32_t alignment) {
void *block;
if (alignment < 4) {
alignment = 4;
}
block = MEMAllocFromDefaultHeapEx(size, alignment);
if (!block) {
OSFatal("AutobootModule: Failed to allocate memory from MEM2");
}
return block;
}
static void FreeMEM2(void *block) {
MEMFreeToDefaultHeap(block);
}
static void *AllocBucket(uint32_t size, uint32_t alignment) {
void *block;
if (!sGfxHeapForeground) {
DEBUG_FUNCTION_LINE_ERR("sGfxHeapForeground was NULL");
return NULL;
}
if (alignment < 4) {
alignment = 4;
}
block = MEMAllocFromExpHeapEx(sGfxHeapForeground, size, alignment);
if (!block) {
DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory from bucket");
}
return block;
}
static void FreeBucket(void *block) {
if (!sGfxHeapForeground) {
DEBUG_FUNCTION_LINE_ERR("sGfxHeapForeground was NULL");
return;
}
MEMFreeToExpHeap(sGfxHeapForeground, block);
}
static void *
GfxGX2RAlloc(GX2RResourceFlags flags, uint32_t size, uint32_t alignment) {
return AllocMEM2(size, alignment);
}
static void
GfxGX2RFree(GX2RResourceFlags flags, void *block) {
return FreeMEM2(block);
}
static void
GfxInitTvColourBuffer(GX2ColorBuffer *cb,
uint32_t width,
uint32_t height,
GX2SurfaceFormat format,
GX2AAMode aa) {
memset(cb, 0, sizeof(GX2ColorBuffer));
cb->surface.use = GX2_SURFACE_USE_TEXTURE_COLOR_BUFFER_TV;
cb->surface.dim = GX2_SURFACE_DIM_TEXTURE_2D;
cb->surface.width = width;
cb->surface.height = height;
cb->surface.depth = 1;
cb->surface.mipLevels = 1;
cb->surface.format = format;
cb->surface.aa = aa;
cb->surface.tileMode = GX2_TILE_MODE_DEFAULT;
cb->viewNumSlices = 1;
GX2CalcSurfaceSizeAndAlignment(&cb->surface);
GX2InitColorBufferRegs(cb);
}
static uint32_t InitMemory() {
// Allocate TV scan buffer.
sTvScanBuffer = AllocBucket(sTvScanBufferSize, GX2_SCAN_BUFFER_ALIGNMENT);
if (!sTvScanBuffer) {
DEBUG_FUNCTION_LINE_INFO("%s: sTvScanBuffer = AllocBucket(0x%X, 0x%X) failed",
__FUNCTION__,
sTvScanBufferSize,
GX2_SCAN_BUFFER_ALIGNMENT);
goto error;
}
GX2Invalidate(GX2_INVALIDATE_MODE_CPU, sTvScanBuffer, sTvScanBufferSize);
GX2SetTVBuffer(sTvScanBuffer, sTvScanBufferSize, sTvRenderMode, sTvSurfaceFormat, GX2_BUFFERING_MODE_SINGLE);
// Allocate TV colour buffer.
sTvColourBuffer.surface.image = AllocMEM2(sTvColourBuffer.surface.imageSize, sTvColourBuffer.surface.alignment);
if (!sTvColourBuffer.surface.image) {
DEBUG_FUNCTION_LINE_INFO("%s: sTvColourBuffer = AllocMEM2(0x%X, 0x%X) failed",
__FUNCTION__,
sTvColourBuffer.surface.imageSize,
sTvColourBuffer.surface.alignment);
goto error;
}
GX2Invalidate(GX2_INVALIDATE_MODE_CPU, sTvColourBuffer.surface.image, sTvColourBuffer.surface.imageSize);
// Allocate DRC scan buffer.
sDrcScanBuffer = AllocBucket(sDrcScanBufferSize, GX2_SCAN_BUFFER_ALIGNMENT);
if (!sDrcScanBuffer) {
DEBUG_FUNCTION_LINE_INFO("%s: sDrcScanBuffer = AllocBucket(0x%X, 0x%X) failed",
__FUNCTION__,
sDrcScanBufferSize,
GX2_SCAN_BUFFER_ALIGNMENT);
goto error;
}
GX2Invalidate(GX2_INVALIDATE_MODE_CPU, sDrcScanBuffer, sDrcScanBufferSize);
GX2SetDRCBuffer(sDrcScanBuffer, sDrcScanBufferSize, sDrcRenderMode, sDrcSurfaceFormat, GX2_BUFFERING_MODE_SINGLE);
// Allocate DRC colour buffer.
sDrcColourBuffer.surface.image = AllocMEM2(sDrcColourBuffer.surface.imageSize, sDrcColourBuffer.surface.alignment);
if (!sDrcColourBuffer.surface.image) {
DEBUG_FUNCTION_LINE_INFO("%s: sDrcColourBuffer = AllocMEM2(0x%X, 0x%X) failed",
__FUNCTION__,
sDrcColourBuffer.surface.imageSize,
sDrcColourBuffer.surface.alignment);
goto error;
}
GX2Invalidate(GX2_INVALIDATE_MODE_CPU, sDrcColourBuffer.surface.image, sDrcColourBuffer.surface.imageSize);
return 0;
error:
return -1;
}
static uint32_t DeinitMemory() {
if (sTvScanBuffer) {
FreeBucket(sTvScanBuffer);
sTvScanBuffer = NULL;
}
if (sTvColourBuffer.surface.image) {
FreeMEM2(sTvColourBuffer.surface.image);
sTvColourBuffer.surface.image = NULL;
}
if (sDrcScanBuffer) {
FreeBucket(sDrcScanBuffer);
sDrcScanBuffer = NULL;
}
if (sDrcColourBuffer.surface.image) {
FreeMEM2(sDrcColourBuffer.surface.image);
sDrcColourBuffer.surface.image = NULL;
}
return 0;
}
static BOOL initBucketHeap() {
MEMHeapHandle heap = MEMGetBaseHeapHandle(MEM_BASE_HEAP_FG);
uint32_t size;
void *base;
size = MEMGetAllocatableSizeForFrmHeapEx(heap, 4);
if (!size) {
DEBUG_FUNCTION_LINE_WARN("%s: MEMAllocFromFrmHeapEx(heap, 0x%X, 4)", __FUNCTION__, size);
return FALSE;
}
base = MEMAllocFromFrmHeapEx(heap, size, 4);
if (!base) {
DEBUG_FUNCTION_LINE_WARN("%s: MEMGetAllocatableSizeForFrmHeapEx == 0", __FUNCTION__);
return FALSE;
}
sGfxHeapForeground = MEMCreateExpHeapEx(base, size, 0);
if (!sGfxHeapForeground) {
DEBUG_FUNCTION_LINE_WARN("%s: MEMCreateExpHeapEx(0x%08X, 0x%X, 0)", __FUNCTION__, base, size);
return FALSE;
}
return TRUE;
}
static BOOL deInitBucketHeap() {
MEMHeapHandle foreground = MEMGetBaseHeapHandle(MEM_BASE_HEAP_FG);
if (sGfxHeapForeground) {
MEMDestroyExpHeap(sGfxHeapForeground);
sGfxHeapForeground = NULL;
}
MEMFreeToFrmHeap(foreground, MEM_FRM_HEAP_FREE_ALL);
return TRUE;
}
BOOL GfxInit() {
initBucketHeap();
uint32_t drcWidth, drcHeight;
uint32_t tvWidth, tvHeight;
uint32_t unk;
sCommandBufferPool = AllocMEM2(WHB_GFX_COMMAND_BUFFER_POOL_SIZE,
GX2_COMMAND_BUFFER_ALIGNMENT);
if (!sCommandBufferPool) {
DEBUG_FUNCTION_LINE_INFO("%s: failed to allocate command buffer pool", __FUNCTION__);
goto error;
}
uint32_t initAttribs[] = {
GX2_INIT_CMD_BUF_BASE, (uintptr_t) sCommandBufferPool,
GX2_INIT_CMD_BUF_POOL_SIZE, WHB_GFX_COMMAND_BUFFER_POOL_SIZE,
GX2_INIT_ARGC, 0,
GX2_INIT_ARGV, 0,
GX2_INIT_END};
GX2Init(initAttribs);
// Clear frame information in saved foreground to avoid screen corruption
__OSClearSavedFrame(OS_SAVED_FRAME_A, OS_SAVED_FRAME_SCREEN_TV);
__OSClearSavedFrame(OS_SAVED_FRAME_A, OS_SAVED_FRAME_SCREEN_DRC);
__OSClearSavedFrame(OS_SAVED_FRAME_B, OS_SAVED_FRAME_SCREEN_TV);
__OSClearSavedFrame(OS_SAVED_FRAME_B, OS_SAVED_FRAME_SCREEN_DRC);
// Disable output until we have rendered something
GX2SetTVEnable(FALSE);
GX2SetDRCEnable(FALSE);
sDrcRenderMode = GX2GetSystemDRCMode();
sTvSurfaceFormat = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8;
sDrcSurfaceFormat = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8;
switch (GX2GetSystemTVScanMode()) {
case GX2_TV_SCAN_MODE_480I:
case GX2_TV_SCAN_MODE_480P:
sTvRenderMode = GX2_TV_RENDER_MODE_WIDE_480P;
tvWidth = 854;
tvHeight = 480;
break;
case GX2_TV_SCAN_MODE_1080I:
case GX2_TV_SCAN_MODE_1080P:
sTvRenderMode = GX2_TV_RENDER_MODE_WIDE_1080P;
tvWidth = 1920;
tvHeight = 1080;
break;
case GX2_TV_SCAN_MODE_720P:
default:
sTvRenderMode = GX2_TV_RENDER_MODE_WIDE_720P;
tvWidth = 1280;
tvHeight = 720;
break;
}
drcWidth = 854;
drcHeight = 480;
// Setup TV and DRC buffers - they will be allocated in GfxProcCallbackAcquired.
GX2CalcTVSize(sTvRenderMode, sTvSurfaceFormat, GX2_BUFFERING_MODE_SINGLE, &sTvScanBufferSize, &unk);
GfxInitTvColourBuffer(&sTvColourBuffer, tvWidth, tvHeight, sTvSurfaceFormat, GX2_AA_MODE1X);
GX2CalcDRCSize(sDrcRenderMode, sDrcSurfaceFormat, GX2_BUFFERING_MODE_DOUBLE, &sDrcScanBufferSize, &unk);
GfxInitTvColourBuffer(&sDrcColourBuffer, drcWidth, drcHeight, sDrcSurfaceFormat, GX2_AA_MODE1X);
GX2CalcDRCSize(sDrcRenderMode, sDrcSurfaceFormat, GX2_BUFFERING_MODE_SINGLE, &sDrcScanBufferSize, &unk);
if (InitMemory() != 0) {
DEBUG_FUNCTION_LINE_INFO("%s: GfxProcCallbackAcquired failed", __FUNCTION__);
goto error;
}
GX2RSetAllocator(&GfxGX2RAlloc, &GfxGX2RFree);
// Initialise TV context state.
sTvContextState = AllocMEM2(sizeof(GX2ContextState), GX2_CONTEXT_STATE_ALIGNMENT);
if (!sTvContextState) {
DEBUG_FUNCTION_LINE_INFO("%s: failed to allocate sTvContextState", __FUNCTION__);
goto error;
}
GX2SetupContextStateEx(sTvContextState, TRUE);
GX2SetContextState(sTvContextState);
GX2SetColorBuffer(&sTvColourBuffer, GX2_RENDER_TARGET_0);
GX2SetViewport(0, 0, (float) sTvColourBuffer.surface.width, (float) sTvColourBuffer.surface.height, 0.0f, 1.0f);
GX2SetScissor(0, 0, (float) sTvColourBuffer.surface.width, (float) sTvColourBuffer.surface.height);
GX2SetTVScale((float) sTvColourBuffer.surface.width, (float) sTvColourBuffer.surface.height);
// Initialise DRC context state.
sDrcContextState = AllocMEM2(sizeof(GX2ContextState), GX2_CONTEXT_STATE_ALIGNMENT);
if (!sDrcContextState) {
WHBLogPrintf("%s: failed to allocate sDrcContextState", __FUNCTION__);
goto error;
}
GX2SetupContextStateEx(sDrcContextState, TRUE);
GX2SetContextState(sDrcContextState);
GX2SetColorBuffer(&sDrcColourBuffer, GX2_RENDER_TARGET_0);
GX2SetViewport(0, 0, (float) sDrcColourBuffer.surface.width, (float) sDrcColourBuffer.surface.height, 0.0f, 1.0f);
GX2SetScissor(0, 0, (float) sDrcColourBuffer.surface.width, (float) sDrcColourBuffer.surface.height);
GX2SetDRCScale((float) sDrcColourBuffer.surface.width, (float) sDrcColourBuffer.surface.height);
// Set 60fps VSync
GX2SetSwapInterval(1);
return TRUE;
error:
if (sCommandBufferPool) {
FreeMEM2(sCommandBufferPool);
sCommandBufferPool = NULL;
}
if (sTvScanBuffer) {
FreeBucket(sTvScanBuffer);
sTvScanBuffer = NULL;
}
if (sTvColourBuffer.surface.image) {
FreeMEM2(sTvColourBuffer.surface.image);
sTvColourBuffer.surface.image = NULL;
}
if (sTvContextState) {
FreeMEM2(sTvContextState);
sTvContextState = NULL;
}
if (sDrcContextState) {
FreeMEM2(sDrcContextState);
sDrcContextState = NULL;
}
if (sDrcScanBuffer) {
FreeBucket(sDrcScanBuffer);
sDrcScanBuffer = NULL;
}
if (sDrcColourBuffer.surface.image) {
FreeMEM2(sDrcColourBuffer.surface.image);
sDrcColourBuffer.surface.image = NULL;
}
return FALSE;
}
void GfxShutdown() {
if (sGpuTimedOut) {
GX2ResetGPU(0);
sGpuTimedOut = FALSE;
}
GX2RSetAllocator(NULL, NULL);
GX2Shutdown();
DeinitMemory();
if (sTvContextState) {
FreeMEM2(sTvContextState);
sTvContextState = NULL;
}
if (sDrcContextState) {
FreeMEM2(sDrcContextState);
sDrcContextState = NULL;
}
if (sCommandBufferPool) {
FreeMEM2(sCommandBufferPool);
sCommandBufferPool = NULL;
}
deInitBucketHeap();
}
void GfxBeginRender() {
uint32_t swapCount, flipCount;
OSTime lastFlip, lastVsync;
uint32_t waitCount = 0;
while (1) {
GX2GetSwapStatus(&swapCount, &flipCount, &lastFlip, &lastVsync);
if (flipCount >= swapCount) {
break;
}
if (waitCount >= 10) {
WHBLogPrint("WHBGfxBeginRender wait for swap timed out");
sGpuTimedOut = TRUE;
break;
}
waitCount++;
GX2WaitForVsync();
}
}
void GfxBeginRenderDRC() {
GX2SetContextState(sDrcContextState);
}
void GfxFinishRenderDRC() {
GX2CopyColorBufferToScanBuffer(&sDrcColourBuffer, GX2_SCAN_TARGET_DRC);
}
void GfxBeginRenderTV() {
GX2SetContextState(sTvContextState);
}
void GfxFinishRenderTV() {
GX2CopyColorBufferToScanBuffer(&sTvColourBuffer, GX2_SCAN_TARGET_TV);
}
void GfxFinishRender() {
GX2SwapScanBuffers();
GX2Flush();
GX2DrawDone();
GX2SetTVEnable(TRUE);
GX2SetDRCEnable(TRUE);
}
BOOL GfxInitFetchShader(WHBGfxShaderGroup *group) {
uint32_t size = GX2CalcFetchShaderSizeEx(group->numAttributes,
GX2_FETCH_SHADER_TESSELLATION_NONE,
GX2_TESSELLATION_MODE_DISCRETE);
group->fetchShaderProgram = AllocMEM2(size, GX2_SHADER_PROGRAM_ALIGNMENT);
GX2InitFetchShaderEx(&group->fetchShader,
group->fetchShaderProgram,
group->numAttributes,
group->attributes,
GX2_FETCH_SHADER_TESSELLATION_NONE,
GX2_TESSELLATION_MODE_DISCRETE);
GX2Invalidate(GX2_INVALIDATE_MODE_CPU_SHADER, group->fetchShaderProgram, size);
return TRUE;
}
static uint32_t
GfxGetAttribFormatSel(GX2AttribFormat format) {
switch (format) {
case GX2_ATTRIB_FORMAT_UNORM_8:
case GX2_ATTRIB_FORMAT_UINT_8:
case GX2_ATTRIB_FORMAT_SNORM_8:
case GX2_ATTRIB_FORMAT_SINT_8:
case GX2_ATTRIB_FORMAT_FLOAT_32:
return GX2_SEL_MASK(GX2_SQ_SEL_X, GX2_SQ_SEL_0, GX2_SQ_SEL_0, GX2_SQ_SEL_1);
case GX2_ATTRIB_FORMAT_UNORM_8_8:
case GX2_ATTRIB_FORMAT_UINT_8_8:
case GX2_ATTRIB_FORMAT_SNORM_8_8:
case GX2_ATTRIB_FORMAT_SINT_8_8:
case GX2_ATTRIB_FORMAT_FLOAT_32_32:
return GX2_SEL_MASK(GX2_SQ_SEL_X, GX2_SQ_SEL_Y, GX2_SQ_SEL_0, GX2_SQ_SEL_1);
case GX2_ATTRIB_FORMAT_FLOAT_32_32_32:
return GX2_SEL_MASK(GX2_SQ_SEL_X, GX2_SQ_SEL_Y, GX2_SQ_SEL_Z, GX2_SQ_SEL_1);
case GX2_ATTRIB_FORMAT_UNORM_8_8_8_8:
case GX2_ATTRIB_FORMAT_UINT_8_8_8_8:
case GX2_ATTRIB_FORMAT_SNORM_8_8_8_8:
case GX2_ATTRIB_FORMAT_SINT_8_8_8_8:
case GX2_ATTRIB_FORMAT_FLOAT_32_32_32_32:
return GX2_SEL_MASK(GX2_SQ_SEL_X, GX2_SQ_SEL_Y, GX2_SQ_SEL_Z, GX2_SQ_SEL_W);
break;
default:
return GX2_SEL_MASK(GX2_SQ_SEL_0, GX2_SQ_SEL_0, GX2_SQ_SEL_0, GX2_SQ_SEL_1);
}
}
static int32_t
GfxGetVertexAttribVarLocation(const GX2VertexShader *shader,
const char *name) {
uint32_t i;
for (i = 0; i < shader->attribVarCount; ++i) {
if (strcmp(shader->attribVars[i].name, name) == 0) {
return shader->attribVars[i].location;
}
}
return -1;
}
BOOL GfxInitShaderAttribute(WHBGfxShaderGroup *group,
const char *name,
uint32_t buffer,
uint32_t offset,
GX2AttribFormat format) {
GX2AttribStream *attrib;
int32_t location;
location = GfxGetVertexAttribVarLocation(group->vertexShader, name);
if (location == -1) {
return FALSE;
}
attrib = &group->attributes[group->numAttributes++];
attrib->location = location;
attrib->buffer = buffer;
attrib->offset = offset;
attrib->format = format;
attrib->type = GX2_ATTRIB_INDEX_PER_VERTEX;
attrib->aluDivisor = 0;
attrib->mask = GfxGetAttribFormatSel(format);
attrib->endianSwap = GX2_ENDIAN_SWAP_DEFAULT;
return TRUE;
}

40
source/utils/gfx.h Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#include <gx2/context.h>
#include <gx2/shaders.h>
#include <gx2/texture.h>
#include <whb/gfx.h>
#include <wut.h>
#ifdef __cplusplus
extern "C" {
#endif
BOOL GfxInit();
void GfxShutdown();
void GfxBeginRender();
void GfxFinishRender();
void GfxBeginRenderDRC();
void GfxFinishRenderDRC();
void GfxBeginRenderTV();
void GfxFinishRenderTV();
BOOL GfxInitShaderAttribute(WHBGfxShaderGroup *group,
const char *name,
uint32_t buffer,
uint32_t offset,
GX2AttribFormat format);
BOOL GfxInitFetchShader(WHBGfxShaderGroup *group);
#ifdef __cplusplus
}
#endif
/** @} */