From a9254a7ee4982c4103b72b89f3133b3c875724c5 Mon Sep 17 00:00:00 2001 From: JaDoggx86 Date: Tue, 15 Nov 2022 19:08:29 +0000 Subject: [PATCH] feat(gpu): add strech render --- CMakeLists.txt | 7 +- game.cpp | 218 +++---------------------------------------------- game.hpp | 1 - src/apu.cpp | 145 ++++++++++++++++++++++++++++++++ src/apu.hpp | 5 ++ src/gpu.cpp | 139 +++++++++++++++++++++++++++++++ 6 files changed, 304 insertions(+), 211 deletions(-) delete mode 100644 game.hpp create mode 100644 src/apu.cpp create mode 100644 src/apu.hpp create mode 100644 src/gpu.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 90a1ff6..4f8a556 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,12 +2,13 @@ cmake_minimum_required(VERSION 3.9) project(blw4) -set(PROJECT_SOURCE game.cpp game.hpp src/backend/wasm_wasm3.c) +set(PROJECT_SOURCE game.cpp src/backend/wasm_wasm3.c) file(GLOB COMMON_SOURCES RELATIVE "${CMAKE_SOURCE_DIR}" "src/*.c") +file(GLOB COMMON_SOURCES_CPP RELATIVE "${CMAKE_SOURCE_DIR}" "src/*.cpp") file(GLOB M3_SOURCES RELATIVE "${CMAKE_SOURCE_DIR}" "vendor/wasm3/source/*.c") -set(PROJECT_DISTRIBS LICENSE 32_BLIT_README.md) +set(PROJECT_DISTRIBS LICENSE README.md) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) @@ -21,7 +22,7 @@ endif() find_package (32BLIT CONFIG REQUIRED PATHS ../32blit-sdk $ENV{PATH_32BLIT_SDK}) -blit_executable (${PROJECT_NAME} ${PROJECT_SOURCE} ${M3_SOURCES} ${COMMON_SOURCES}) +blit_executable (${PROJECT_NAME} ${PROJECT_SOURCE} ${M3_SOURCES} ${COMMON_SOURCES} ${COMMON_SOURCES_CPP}) blit_assets_yaml (${PROJECT_NAME} assets.yml) blit_metadata (${PROJECT_NAME} metadata.yml) add_custom_target (flash DEPENDS ${PROJECT_NAME}.flash) diff --git a/game.cpp b/game.cpp index cf9cf2a..ed0d109 100644 --- a/game.cpp +++ b/game.cpp @@ -1,8 +1,7 @@ -#include "game.hpp" +#include "32blit.hpp" +#include "src/apu.hpp" #include -#include #include -#include extern "C" { #include "src/runtime.h" @@ -13,8 +12,8 @@ static w4_Disk disk_storage_data{0, {}}; static bool cart_loaded = false; static bool first_time = true; static uint32_t prev_time_ms = 0; -double fps = 0.0; -std::stringstream temp_sstream; + +blit::File cart_file{}; // Note: This code is taken from Daft-Freak's DaftBoy32 // This creates a flash storage that you can upload the .wasm file to @@ -55,15 +54,11 @@ void initialize_filesystem() { dataPtr += filenameLength + fileLength; } } -blit::File cart_file{}; void init() { blit::set_screen_mode(blit::ScreenMode::hires); initialize_filesystem(); - blit::channels[0].waveforms = blit::Waveform::SQUARE; - blit::channels[1].waveforms = blit::Waveform::SQUARE; - blit::channels[2].waveforms = blit::Waveform::TRIANGLE; - blit::channels[3].waveforms = blit::Waveform::NOISE; + init_apu(); char *cart_bytes; size_t cart_length = 0; uint8_t *memory = w4_wasmInit(); @@ -123,198 +118,9 @@ void render(uint32_t time) { blit::Point(5, 4)); } else { w4_runtimeDraw(); - blit::screen.pen = blit::Pen(255, 255, 255); - blit::screen.rectangle(blit::Rect(0, 160, 320, 14)); - blit::screen.pen = blit::Pen(255, 0, 0); - blit::screen.text("Running: cart.wasm", blit::minimal_font, - blit::Point(5, 164)); - blit::screen.pen = blit::Pen(255, 0, 0); - temp_sstream.str(""); - temp_sstream.clear(); - temp_sstream << "FPS: " << std::fixed << std::setprecision(2) << fps; - blit::screen.text(temp_sstream.str(), blit::minimal_font, - blit::Point(160, 165)); - } -} - -uint8_t* rgb(uint8_t* target, uint32_t colour) { - uint8_t r = (((colour & 0xff0000) >> 8) >> 8); - uint8_t g = ((colour & 0xff00) >> 8); - uint8_t b = (colour & 0xff); - *target++ = r; - *target++ = g; - *target++ = b; - return target; -} - -extern "C" { -void w4_windowComposite(const uint32_t *palette, const uint8_t *framebuffer) { - int pixel = -1; - for (int n = 0; n < 160 * 160 / 4; ++n) { - uint8_t quartet = framebuffer[n]; - int c1 = (quartet & 0b00000011) >> 0; - int c2 = (quartet & 0b00001100) >> 2; - int c3 = (quartet & 0b00110000) >> 4; - int c4 = (quartet & 0b11000000) >> 6; - - pixel++; - int y = pixel / 160; - int x = pixel % 160; - rgb(blit::screen.ptr(x, y), palette[c1]); - - pixel++; - y = pixel / 160; - x = pixel % 160; - rgb(blit::screen.ptr(x, y), palette[c2]); - - pixel++; - y = pixel / 160; - x = pixel % 160; - rgb(blit::screen.ptr(x, y), palette[c3]); - - pixel++; - y = pixel / 160; - x = pixel % 160; - rgb(blit::screen.ptr(x, y), palette[c4]); - } } -} - - -int g_min(int x, int y) { return x < y ? x : y; } -int g_max(int x, int y) { return x > y ? x : y; } - -enum class AUDIO_STEP { - N /*Nothing*/, - A_START, - A_HOLD, - D_START, - D_HOLD, - S_START, - S_HOLD, - R_START, - R_HOLD -}; -int attack_ms[4] = {0, 0, 0, 0}; -int decay_ms[4] = {0, 0, 0, 0}; -int sustain_ms[4] = {0, 0, 0, 0}; -int release_ms[4] = {0, 0, 0, 0}; -AUDIO_STEP audio_task[4] = {AUDIO_STEP::N, AUDIO_STEP::N, AUDIO_STEP::N, - AUDIO_STEP::N}; - -extern "C" { -void wasm4_tone_callback(uint32_t frequency, uint32_t duration, uint32_t volume, - uint32_t flags) { - int freq1 = frequency & 0xffff; - int freq2 = (frequency >> 16) & 0xffff; - int sustain = duration & 0xff; - int release = (duration >> 8) & 0xff; - int decay = (duration >> 16) & 0xff; - int attack = (duration >> 24) & 0xff; - - int sustainVolume = g_min(volume & 0xff, 100); - int peakVolume = g_min((volume >> 8) & 0xff, 100); - - int channelIdx = flags & 0x03; - int mode = (flags >> 2) & 0x3; // TODO - int pan = (flags >> 4) & 0x3; // Cannot be done unless we support stereo - blit::channels[channelIdx].frequency = freq1; - blit::channels[channelIdx].filter_cutoff_frequency = freq2; - blit::channels[channelIdx].sustain = 0xffff; // * sustainVolume / 100; - blit::channels[channelIdx].volume = 0xffff; //* peakVolume / 100; - blit::channels[channelIdx].attack_ms = g_max(attack * 1000 / fps, 1); - blit::channels[channelIdx].decay_ms = g_max(decay * 1000 / fps, 1); - blit::channels[channelIdx].release_ms = g_max(release * 1000 / fps, 1); - attack_ms[channelIdx] = g_max(attack * 1000 / fps, 1); - decay_ms[channelIdx] = g_max(decay * 1000 / fps, 1); - sustain_ms[channelIdx] = g_max(sustain * 1000 / fps, 1); - release_ms[channelIdx] = g_max(release * 1000 / fps, 1); - audio_task[channelIdx] = AUDIO_STEP::A_START; -} -} - -void play_audio(uint32_t time_ms) { - int delta = 0; - if (first_time) { - first_time = false; - } else { - delta = time_ms - prev_time_ms; - prev_time_ms = time_ms; - } - for (int chan = 0; chan < 4; chan++) { - if (audio_task[chan] == AUDIO_STEP::N) { - continue; - } - // ATTACK - if (audio_task[chan] == AUDIO_STEP::A_START) { - if (attack_ms[chan] <= 0) { - audio_task[chan] = AUDIO_STEP::D_START; - } else { - audio_task[chan] = AUDIO_STEP::A_HOLD; - blit::channels[chan].trigger_attack(); - } - continue; - } - if (audio_task[chan] == AUDIO_STEP::A_HOLD) { - attack_ms[chan] -= delta; - if (attack_ms[chan] <= 0) { - audio_task[chan] = AUDIO_STEP::D_START; - } - } - // DECAY - if (audio_task[chan] == AUDIO_STEP::D_START) { - if (decay_ms[chan] <= 0) { - audio_task[chan] = AUDIO_STEP::S_START; - } else { - audio_task[chan] = AUDIO_STEP::D_HOLD; - blit::channels[chan].trigger_decay(); - } - continue; - } - if (audio_task[chan] == AUDIO_STEP::D_HOLD) { - decay_ms[chan] -= delta; - if (decay_ms[chan] <= 0) { - audio_task[chan] = AUDIO_STEP::S_START; - } - } - // SUSTAIN - if (audio_task[chan] == AUDIO_STEP::S_START) { - if (sustain_ms[chan] <= 0) { - audio_task[chan] = AUDIO_STEP::R_START; - } else { - audio_task[chan] = AUDIO_STEP::S_HOLD; - blit::channels[chan].trigger_sustain(); - } - continue; - } - if (audio_task[chan] == AUDIO_STEP::S_HOLD) { - sustain_ms[chan] -= delta; - if (sustain_ms[chan] <= 0) { - audio_task[chan] = AUDIO_STEP::R_START; - } - } - // RELEASE - if (audio_task[chan] == AUDIO_STEP::R_START) { - if (release_ms[chan] <= 0) { - audio_task[chan] = AUDIO_STEP::N; - blit::channels[chan].off(); - } else { - audio_task[chan] = AUDIO_STEP::R_HOLD; - blit::channels[chan].trigger_release(); - } - continue; - } - if (audio_task[chan] == AUDIO_STEP::R_HOLD) { - release_ms[chan] -= delta; - if (release_ms[chan] <= 0) { - audio_task[chan] = AUDIO_STEP::N; - blit::channels[chan].off(); - } - } - } -} void capture_input() { // Set player 1 gamepad uint8_t gamepad = 0; @@ -346,14 +152,12 @@ void capture_input() { // Set player 1 gamepad } void update(uint32_t time) { - if (!cart_loaded) { - return; - } capture_input(); - if (!first_time) { - float delta_ms = time - prev_time_ms; - fps = (1.0 / static_cast(delta_ms)) * 1000.0; - } w4_runtimeUpdate(); - play_audio(time); + play_audio(time, prev_time_ms, first_time); + if (first_time) { + first_time = false; + } else { + prev_time_ms = time; + } } diff --git a/game.hpp b/game.hpp deleted file mode 100644 index 8ca0a7e..0000000 --- a/game.hpp +++ /dev/null @@ -1 +0,0 @@ -#include "32blit.hpp" diff --git a/src/apu.cpp b/src/apu.cpp new file mode 100644 index 0000000..4c5cb89 --- /dev/null +++ b/src/apu.cpp @@ -0,0 +1,145 @@ +#include "32blit.hpp" + +static double fps = 0.0; + +enum class AUDIO_STEP { + N /*Nothing*/, + A_START, + A_HOLD, + D_START, + D_HOLD, + S_START, + S_HOLD, + R_START, + R_HOLD +}; + +static int attack_ms[4] = {0, 0, 0, 0}; +static int decay_ms[4] = {0, 0, 0, 0}; +static int sustain_ms[4] = {0, 0, 0, 0}; +static int release_ms[4] = {0, 0, 0, 0}; +static AUDIO_STEP audio_task[4] = {AUDIO_STEP::N, AUDIO_STEP::N, AUDIO_STEP::N, + AUDIO_STEP::N}; + +int g_min(int x, int y) { return x < y ? x : y; } +int g_max(int x, int y) { return x > y ? x : y; } + +extern "C" { +void wasm4_tone_callback(uint32_t frequency, uint32_t duration, uint32_t volume, + uint32_t flags) { + int freq1 = frequency & 0xffff; + int freq2 = (frequency >> 16) & 0xffff; + + int sustain = duration & 0xff; + int release = (duration >> 8) & 0xff; + int decay = (duration >> 16) & 0xff; + int attack = (duration >> 24) & 0xff; + + int sustainVolume = g_min(volume & 0xff, 100); + int peakVolume = g_min((volume >> 8) & 0xff, 100); + + int channelIdx = flags & 0x03; + int mode = (flags >> 2) & 0x3; // TODO + int pan = (flags >> 4) & 0x3; // Cannot be done unless we support stereo + blit::channels[channelIdx].frequency = freq1; + blit::channels[channelIdx].filter_cutoff_frequency = freq2; + blit::channels[channelIdx].sustain = 0xffff; // * sustainVolume / 100; + blit::channels[channelIdx].volume = 0xffff; //* peakVolume / 100; + blit::channels[channelIdx].attack_ms = g_max(attack * 1000 / fps, 1); + blit::channels[channelIdx].decay_ms = g_max(decay * 1000 / fps, 1); + blit::channels[channelIdx].release_ms = g_max(release * 1000 / fps, 1); + attack_ms[channelIdx] = g_max(attack * 1000 / fps, 1); + decay_ms[channelIdx] = g_max(decay * 1000 / fps, 1); + sustain_ms[channelIdx] = g_max(sustain * 1000 / fps, 1); + release_ms[channelIdx] = g_max(release * 1000 / fps, 1); + audio_task[channelIdx] = AUDIO_STEP::A_START; +} +} + +void init_apu() { + blit::channels[0].waveforms = blit::Waveform::SQUARE; + blit::channels[1].waveforms = blit::Waveform::SQUARE; + blit::channels[2].waveforms = blit::Waveform::TRIANGLE; + blit::channels[3].waveforms = blit::Waveform::NOISE; +} + +double get_fps() { return fps; } + +void play_audio(uint32_t time_ms, uint32_t prev_time_ms, bool first_time) { + int delta = 0; + if (!first_time) { + delta = time_ms - prev_time_ms; + fps = (1.0 / delta) * 1000.0; + } + for (int chan = 0; chan < 4; chan++) { + if (audio_task[chan] == AUDIO_STEP::N) { + continue; + } + // ATTACK + if (audio_task[chan] == AUDIO_STEP::A_START) { + if (attack_ms[chan] <= 0) { + audio_task[chan] = AUDIO_STEP::D_START; + } else { + audio_task[chan] = AUDIO_STEP::A_HOLD; + blit::channels[chan].trigger_attack(); + } + continue; + } + if (audio_task[chan] == AUDIO_STEP::A_HOLD) { + attack_ms[chan] -= delta; + if (attack_ms[chan] <= 0) { + audio_task[chan] = AUDIO_STEP::D_START; + } + } + // DECAY + if (audio_task[chan] == AUDIO_STEP::D_START) { + if (decay_ms[chan] <= 0) { + audio_task[chan] = AUDIO_STEP::S_START; + } else { + audio_task[chan] = AUDIO_STEP::D_HOLD; + blit::channels[chan].trigger_decay(); + } + continue; + } + if (audio_task[chan] == AUDIO_STEP::D_HOLD) { + decay_ms[chan] -= delta; + if (decay_ms[chan] <= 0) { + audio_task[chan] = AUDIO_STEP::S_START; + } + } + // SUSTAIN + if (audio_task[chan] == AUDIO_STEP::S_START) { + if (sustain_ms[chan] <= 0) { + audio_task[chan] = AUDIO_STEP::R_START; + } else { + audio_task[chan] = AUDIO_STEP::S_HOLD; + blit::channels[chan].trigger_sustain(); + } + continue; + } + if (audio_task[chan] == AUDIO_STEP::S_HOLD) { + sustain_ms[chan] -= delta; + if (sustain_ms[chan] <= 0) { + audio_task[chan] = AUDIO_STEP::R_START; + } + } + // RELEASE + if (audio_task[chan] == AUDIO_STEP::R_START) { + if (release_ms[chan] <= 0) { + audio_task[chan] = AUDIO_STEP::N; + blit::channels[chan].off(); + } else { + audio_task[chan] = AUDIO_STEP::R_HOLD; + blit::channels[chan].trigger_release(); + } + continue; + } + if (audio_task[chan] == AUDIO_STEP::R_HOLD) { + release_ms[chan] -= delta; + if (release_ms[chan] <= 0) { + audio_task[chan] = AUDIO_STEP::N; + blit::channels[chan].off(); + } + } + } +} diff --git a/src/apu.hpp b/src/apu.hpp new file mode 100644 index 0000000..de11455 --- /dev/null +++ b/src/apu.hpp @@ -0,0 +1,5 @@ +#pragma once +#include +void play_audio(uint32_t time_ms, uint32_t prev_time_ms, bool first_time); +void init_apu(); +double get_fps(); diff --git a/src/gpu.cpp b/src/gpu.cpp new file mode 100644 index 0000000..93ac88f --- /dev/null +++ b/src/gpu.cpp @@ -0,0 +1,139 @@ +#include "32blit.hpp" +#include + +#define WASM4_SIZE 160 +#define TARGET_SIZE 240 +#if defined(PICO_BUILD) +#define TARGET_WIDTH 240 +#else +#define TARGET_WIDTH 320 +#endif +#define WASM4_PIXELS_PER_BYTE 4 + +/** + * Write rgb to given pointer (3 bytes are written) + * @param target + * @param colour + * @return + */ +uint8_t *rgb(uint8_t *target, uint32_t colour) { + uint8_t r = (((colour & 0xff0000) >> 8) >> 8); + uint8_t g = ((colour & 0xff00) >> 8); + uint8_t b = (colour & 0xff); + *target++ = r; + *target++ = g; + *target++ = b; + return target; +} + +uint32_t blend(uint32_t colour1, uint32_t colour2) { + uint8_t r1 = (((colour1 & 0xff0000) >> 8) >> 8); + uint8_t g1 = ((colour1 & 0xff00) >> 8); + uint8_t b1 = (colour1 & 0xff); + uint8_t r2 = (((colour2 & 0xff0000) >> 8) >> 8); + uint8_t g2 = ((colour2 & 0xff00) >> 8); + uint8_t b2 = (colour2 & 0xff); + auto r = (uint8_t)(0.5 * r1 + 0.5 * r2); + auto g = (uint8_t)(0.5 * g1 + 0.5 * g2); + auto b = (uint8_t)(0.5 * b1 + 0.5 * b2); + return b | (g << 8) | ((r << 8) << 8); +} + +#ifndef PICO_BUILD +static uint32_t prev_row[TARGET_SIZE]; +static uint32_t cur_row[TARGET_SIZE]; +#endif +static int target_x = 0; +static int target_y = -1; +const int x_skip = (TARGET_WIDTH - TARGET_SIZE) / 2; +void draw_point_target(int x, int y, uint32_t colour) { + rgb(blit::screen.ptr(x_skip + x, y), colour); +} + +void draw_point(int origin_x, int origin_y, uint32_t origin_colour) { + int x, y; + bool row_fill = false; + if (origin_x == 0) { +#ifndef PICO_BUILD + for (int pos = 0; pos < TARGET_SIZE; pos++) { + prev_row[pos] = cur_row[pos]; + } +#endif + target_x = 0; + target_y++; + if ((origin_y + 1) % 2 == 0) { + target_y++; + row_fill = true; + } + } else { + target_x++; + } + if (target_y >= TARGET_SIZE) { + target_y = 0; + } + uint32_t colour = origin_colour; + y = target_y; + if ((origin_x + 1) % 2 == 0) { +#ifndef PICO_BUILD + uint32_t prev_colour = cur_row[target_x - 1]; + uint32_t blended = blend(prev_colour, colour); + draw_point_target(target_x, y, blended); + cur_row[target_x] = blended; +#endif + target_x++; + } + x = target_x; + draw_point_target(x, y, colour); +#ifndef PICO_BUILD + cur_row[x] = colour; + if (row_fill) { + int row_to_fill = y - 1; + for (int fill_x = 0; fill_x < TARGET_SIZE; fill_x++) { + uint32_t blended_colour = blend(prev_row[fill_x], cur_row[fill_x]); + draw_point_target(fill_x, row_to_fill, blended_colour); + } + } +#endif +} + +extern "C" { +/** + * callback for wasm4 drawing + * @param palette + * @param framebuffer + */ +void wasm4_draw_callback(const uint32_t *palette, const uint8_t *framebuffer) { + int pixel = -1, c1, c2, c3, c4, x, y; + const int framebuffer_items = WASM4_SIZE * WASM4_SIZE / WASM4_PIXELS_PER_BYTE; + for (int n = 0; n < framebuffer_items; ++n) { + uint8_t quartet = framebuffer[n]; + c1 = (quartet & 0b00000011) >> 0; + c2 = (quartet & 0b00001100) >> 2; + c3 = (quartet & 0b00110000) >> 4; + c4 = (quartet & 0b11000000) >> 6; + + pixel++; + y = pixel / WASM4_SIZE; + x = pixel % WASM4_SIZE; + draw_point(x, y, palette[c1]); + + pixel++; + y = pixel / WASM4_SIZE; + x = pixel % WASM4_SIZE; + draw_point(x, y, palette[c2]); + + pixel++; + y = pixel / WASM4_SIZE; + x = pixel % WASM4_SIZE; + draw_point(x, y, palette[c3]); + + pixel++; + y = pixel / WASM4_SIZE; + x = pixel % WASM4_SIZE; + draw_point(x, y, palette[c4]); + } +} +void w4_windowComposite(const uint32_t *palette, const uint8_t *framebuffer) { + wasm4_draw_callback(palette, framebuffer); +} +}