epic: refactor SDL2_Renderer usage to IRenderer and queued texture rendering
- Updated the testing guide to clarify the testing framework's organization and execution methods, improving user understanding. - Refactored CMakeLists to include new platform-specific files, ensuring proper integration of the rendering backend. - Modified main application files to utilize the new IRenderer interface, enhancing flexibility in rendering operations. - Implemented deferred texture management in various components, allowing for more efficient graphics handling and improved performance. - Introduced new methods for texture creation and updates, streamlining the rendering process across the application. - Enhanced logging and error handling in the rendering pipeline to facilitate better debugging and diagnostics.
This commit is contained in:
@@ -342,7 +342,7 @@ if(YAZE_BUILD_EMU)
|
||||
MACOSX_BUNDLE
|
||||
app/emu/emu.cc
|
||||
app/rom.cc
|
||||
app/core/platform/app_delegate.mm
|
||||
app/platform/app_delegate.mm
|
||||
${YAZE_APP_EMU_SRC}
|
||||
${YAZE_APP_CORE_SRC}
|
||||
${YAZE_APP_EDITOR_SRC}
|
||||
@@ -746,18 +746,18 @@ source_group("Application\\Core\\Asar" FILES
|
||||
|
||||
# Platform-specific files
|
||||
source_group("Application\\Core\\Platform" FILES
|
||||
app/core/platform/app_delegate.h
|
||||
app/core/platform/app_delegate.mm
|
||||
app/core/platform/clipboard.cc
|
||||
app/core/platform/clipboard.h
|
||||
app/core/platform/clipboard.mm
|
||||
app/core/platform/file_dialog.cc
|
||||
app/core/platform/file_dialog.h
|
||||
app/core/platform/file_dialog.mm
|
||||
app/core/platform/font_loader.cc
|
||||
app/core/platform/font_loader.h
|
||||
app/core/platform/font_loader.mm
|
||||
app/core/platform/view_controller.h
|
||||
app/platform/app_delegate.h
|
||||
app/platform/app_delegate.mm
|
||||
app/platform/clipboard.cc
|
||||
app/platform/clipboard.h
|
||||
app/platform/clipboard.mm
|
||||
app/platform/file_dialog.cc
|
||||
app/platform/file_dialog.h
|
||||
app/platform/file_dialog.mm
|
||||
app/platform/font_loader.cc
|
||||
app/platform/font_loader.h
|
||||
app/platform/font_loader.mm
|
||||
app/platform/view_controller.h
|
||||
)
|
||||
|
||||
# Editor System
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "app/core/window.h"
|
||||
#include "app/editor/editor_manager.h"
|
||||
#include "app/editor/ui/background_renderer.h"
|
||||
#include "app/gfx/arena.h" // Add include for Arena
|
||||
#include "app/gfx/backend/sdl2_renderer.h" // Add include for new renderer
|
||||
#include "app/gui/theme_manager.h"
|
||||
#include "app/gui/widgets/widget_id_registry.h"
|
||||
#include "imgui/backends/imgui_impl_sdl2.h"
|
||||
@@ -16,10 +18,21 @@ namespace yaze {
|
||||
namespace core {
|
||||
|
||||
absl::Status Controller::OnEntry(std::string filename) {
|
||||
RETURN_IF_ERROR(CreateWindow(window_, SDL_WINDOW_RESIZABLE));
|
||||
// Create renderer FIRST
|
||||
renderer_ = std::make_unique<gfx::SDL2Renderer>();
|
||||
|
||||
// Call CreateWindow with our renderer
|
||||
RETURN_IF_ERROR(CreateWindow(window_, renderer_.get(), SDL_WINDOW_RESIZABLE));
|
||||
|
||||
// Initialize the graphics Arena with the renderer
|
||||
gfx::Arena::Get().Initialize(renderer_.get());
|
||||
|
||||
// Set up audio for emulator
|
||||
editor_manager_.emulator().set_audio_buffer(window_.audio_buffer_.get());
|
||||
editor_manager_.emulator().set_audio_device_id(window_.audio_device_);
|
||||
editor_manager_.Initialize(filename);
|
||||
|
||||
// Initialize editor manager with renderer
|
||||
editor_manager_.Initialize(renderer_.get(), filename);
|
||||
active_ = true;
|
||||
return absl::OkStatus();
|
||||
}
|
||||
@@ -74,14 +87,20 @@ absl::Status Controller::OnLoad() {
|
||||
}
|
||||
|
||||
void Controller::DoRender() const {
|
||||
// Process all pending texture commands.
|
||||
gfx::Arena::Get().ProcessTextureQueue(renderer_.get());
|
||||
|
||||
ImGui::Render();
|
||||
SDL_RenderClear(Renderer::Get().renderer());
|
||||
renderer_->Clear();
|
||||
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(),
|
||||
Renderer::Get().renderer());
|
||||
SDL_RenderPresent(Renderer::Get().renderer());
|
||||
static_cast<SDL_Renderer*>(renderer_->GetBackendRenderer()));
|
||||
renderer_->Present();
|
||||
}
|
||||
|
||||
void Controller::OnExit() { PRINT_IF_ERROR(ShutdownWindow(window_)); }
|
||||
void Controller::OnExit() {
|
||||
renderer_->Shutdown();
|
||||
PRINT_IF_ERROR(ShutdownWindow(window_));
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
} // namespace yaze
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "absl/status/status.h"
|
||||
#include "app/core/window.h"
|
||||
#include "app/editor/editor_manager.h"
|
||||
#include "app/gfx/backend/irenderer.h"
|
||||
|
||||
int main(int argc, char** argv);
|
||||
|
||||
@@ -36,6 +37,7 @@ class Controller {
|
||||
return editor_manager_.overworld();
|
||||
}
|
||||
auto GetCurrentRom() -> Rom* { return editor_manager_.GetCurrentRom(); }
|
||||
auto renderer() -> gfx::IRenderer* { return renderer_.get(); }
|
||||
|
||||
private:
|
||||
friend int ::main(int argc, char** argv);
|
||||
@@ -43,6 +45,7 @@ class Controller {
|
||||
bool active_ = false;
|
||||
core::Window window_;
|
||||
editor::EditorManager editor_manager_;
|
||||
std::unique_ptr<gfx::IRenderer> renderer_;
|
||||
};
|
||||
|
||||
} // namespace core
|
||||
|
||||
@@ -9,18 +9,18 @@ set(
|
||||
|
||||
if (WIN32 OR MINGW OR (UNIX AND NOT APPLE))
|
||||
list(APPEND YAZE_APP_CORE_SRC
|
||||
app/core/platform/font_loader.cc
|
||||
app/core/platform/asset_loader.cc
|
||||
app/platform/font_loader.cc
|
||||
app/platform/asset_loader.cc
|
||||
)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND YAZE_APP_CORE_SRC
|
||||
app/core/platform/file_dialog.mm
|
||||
app/core/platform/app_delegate.mm
|
||||
app/core/platform/font_loader.cc
|
||||
app/core/platform/font_loader.mm
|
||||
app/core/platform/asset_loader.cc
|
||||
app/platform/file_dialog.mm
|
||||
app/platform/app_delegate.mm
|
||||
app/platform/font_loader.cc
|
||||
app/platform/font_loader.mm
|
||||
app/platform/asset_loader.cc
|
||||
)
|
||||
|
||||
find_library(COCOA_LIBRARY Cocoa)
|
||||
|
||||
@@ -28,9 +28,9 @@ YazeGRPCServer::~YazeGRPCServer() {
|
||||
absl::Status YazeGRPCServer::Initialize(
|
||||
int port,
|
||||
test::TestManager* test_manager,
|
||||
app::Rom* rom,
|
||||
app::net::RomVersionManager* version_mgr,
|
||||
app::net::ProposalApprovalManager* approval_mgr,
|
||||
Rom* rom,
|
||||
net::RomVersionManager* version_mgr,
|
||||
net::ProposalApprovalManager* approval_mgr,
|
||||
CanvasAutomationServiceImpl* canvas_service) {
|
||||
|
||||
if (is_running_) {
|
||||
@@ -50,11 +50,11 @@ absl::Status YazeGRPCServer::Initialize(
|
||||
|
||||
// Create ROM service if rom provided
|
||||
if (config_.enable_rom_service && rom) {
|
||||
rom_service_ = std::make_unique<app::net::RomServiceImpl>(
|
||||
rom_service_ = std::make_unique<net::RomServiceImpl>(
|
||||
rom, version_mgr, approval_mgr);
|
||||
|
||||
// Configure ROM service
|
||||
app::net::RomServiceImpl::Config rom_config;
|
||||
net::RomServiceImpl::Config rom_config;
|
||||
rom_config.require_approval_for_writes = config_.require_approval_for_rom_writes;
|
||||
rom_service_->SetConfig(rom_config);
|
||||
|
||||
|
||||
@@ -20,13 +20,13 @@ namespace yaze {
|
||||
// Forward declarations
|
||||
class CanvasAutomationServiceImpl;
|
||||
|
||||
namespace app {
|
||||
|
||||
class Rom;
|
||||
namespace net {
|
||||
class ProposalApprovalManager;
|
||||
class RomServiceImpl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace test {
|
||||
class TestManager;
|
||||
@@ -85,9 +85,9 @@ class YazeGRPCServer {
|
||||
absl::Status Initialize(
|
||||
int port,
|
||||
test::TestManager* test_manager = nullptr,
|
||||
app::Rom* rom = nullptr,
|
||||
app::net::RomVersionManager* version_mgr = nullptr,
|
||||
app::net::ProposalApprovalManager* approval_mgr = nullptr,
|
||||
Rom* rom = nullptr,
|
||||
net::RomVersionManager* version_mgr = nullptr,
|
||||
net::ProposalApprovalManager* approval_mgr = nullptr,
|
||||
CanvasAutomationServiceImpl* canvas_service = nullptr);
|
||||
|
||||
/**
|
||||
@@ -126,7 +126,7 @@ class YazeGRPCServer {
|
||||
Config config_;
|
||||
std::unique_ptr<grpc::Server> server_;
|
||||
std::unique_ptr<test::ImGuiTestHarnessServiceImpl> test_harness_service_;
|
||||
std::unique_ptr<app::net::RomServiceImpl> rom_service_;
|
||||
std::unique_ptr<net::RomServiceImpl> rom_service_;
|
||||
std::unique_ptr<CanvasAutomationServiceImpl> canvas_service_;
|
||||
// Store as base grpc::Service* to avoid incomplete type issues
|
||||
std::unique_ptr<grpc::Service> canvas_grpc_service_;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/core/platform/font_loader.h"
|
||||
#include "app/platform/font_loader.h"
|
||||
#include "util/sdl_deleter.h"
|
||||
#include "util/log.h"
|
||||
#include "app/gfx/arena.h"
|
||||
@@ -51,7 +51,7 @@ void ImGuiAssertionHandler(const char* expr, const char* file, int line,
|
||||
namespace yaze {
|
||||
namespace core {
|
||||
|
||||
absl::Status CreateWindow(Window& window, int flags) {
|
||||
absl::Status CreateWindow(Window& window, gfx::IRenderer* renderer, int flags) {
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) != 0) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("SDL_Init: %s\n", SDL_GetError()));
|
||||
@@ -72,7 +72,12 @@ absl::Status CreateWindow(Window& window, int flags) {
|
||||
absl::StrFormat("SDL_CreateWindow: %s\n", SDL_GetError()));
|
||||
}
|
||||
|
||||
RETURN_IF_ERROR(Renderer::Get().CreateRenderer(window.window_.get()));
|
||||
// Only initialize renderer if one is provided and not already initialized
|
||||
if (renderer && !renderer->GetBackendRenderer()) {
|
||||
if (!renderer->Initialize(window.window_.get())) {
|
||||
return absl::InternalError("Failed to initialize renderer");
|
||||
}
|
||||
}
|
||||
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
@@ -93,30 +98,37 @@ absl::Status CreateWindow(Window& window, int flags) {
|
||||
test::TestManager::Get().InitializeUITesting();
|
||||
#endif
|
||||
|
||||
ImGui_ImplSDL2_InitForSDLRenderer(window.window_.get(),
|
||||
Renderer::Get().renderer());
|
||||
ImGui_ImplSDLRenderer2_Init(Renderer::Get().renderer());
|
||||
// Initialize ImGui backends if renderer is provided
|
||||
if (renderer) {
|
||||
SDL_Renderer* sdl_renderer = static_cast<SDL_Renderer*>(renderer->GetBackendRenderer());
|
||||
ImGui_ImplSDL2_InitForSDLRenderer(window.window_.get(), sdl_renderer);
|
||||
ImGui_ImplSDLRenderer2_Init(sdl_renderer);
|
||||
}
|
||||
|
||||
RETURN_IF_ERROR(LoadPackageFonts());
|
||||
|
||||
// Apply original YAZE colors as fallback, then try to load theme system
|
||||
gui::ColorsYaze();
|
||||
|
||||
const int audio_frequency = 48000;
|
||||
SDL_AudioSpec want, have;
|
||||
SDL_memset(&want, 0, sizeof(want));
|
||||
want.freq = audio_frequency;
|
||||
want.format = AUDIO_S16;
|
||||
want.channels = 2;
|
||||
want.samples = 2048;
|
||||
want.callback = NULL; // Uses the queue
|
||||
window.audio_device_ = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0);
|
||||
// Initialize audio if not already initialized
|
||||
if (window.audio_device_ == 0) {
|
||||
throw std::runtime_error(
|
||||
absl::StrFormat("Failed to open audio: %s\n", SDL_GetError()));
|
||||
const int audio_frequency = 48000;
|
||||
SDL_AudioSpec want, have;
|
||||
SDL_memset(&want, 0, sizeof(want));
|
||||
want.freq = audio_frequency;
|
||||
want.format = AUDIO_S16;
|
||||
want.channels = 2;
|
||||
want.samples = 2048;
|
||||
want.callback = NULL; // Uses the queue
|
||||
window.audio_device_ = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0);
|
||||
if (window.audio_device_ == 0) {
|
||||
LOG_ERROR("Window", "Failed to open audio: %s", SDL_GetError());
|
||||
// Don't fail - audio is optional
|
||||
} else {
|
||||
window.audio_buffer_ = std::make_shared<int16_t>(audio_frequency / 50 * 4);
|
||||
SDL_PauseAudioDevice(window.audio_device_, 0);
|
||||
}
|
||||
}
|
||||
window.audio_buffer_ = std::make_shared<int16_t>(audio_frequency / 50 * 4);
|
||||
SDL_PauseAudioDevice(window.audio_device_, 0);
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
@@ -129,12 +141,20 @@ absl::Status ShutdownWindow(Window& window) {
|
||||
#ifdef YAZE_ENABLE_IMGUI_TEST_ENGINE
|
||||
test::TestManager::Get().StopUITesting();
|
||||
#endif
|
||||
|
||||
// TODO: BAD FIX, SLOW SHUTDOWN TAKES TOO LONG NOW
|
||||
// CRITICAL FIX: Shutdown graphics arena FIRST
|
||||
// This ensures all textures are destroyed while renderer is still valid
|
||||
LOG_INFO("Window", "Shutting down graphics arena...");
|
||||
gfx::Arena::Get().Shutdown();
|
||||
|
||||
// Shutdown ImGui implementations
|
||||
// Shutdown ImGui implementations (after Arena but before context)
|
||||
LOG_INFO("Window", "Shutting down ImGui implementations...");
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
ImGui_ImplSDLRenderer2_Shutdown();
|
||||
|
||||
// Destroy ImGui context
|
||||
LOG_INFO("Window", "Destroying ImGui context...");
|
||||
ImGui::DestroyContext();
|
||||
|
||||
// NOW destroy test engine context (after ImGui context is destroyed)
|
||||
@@ -142,12 +162,14 @@ absl::Status ShutdownWindow(Window& window) {
|
||||
test::TestManager::Get().DestroyUITestingContext();
|
||||
#endif
|
||||
|
||||
// Shutdown graphics arena BEFORE destroying SDL contexts
|
||||
gfx::Arena::Get().Shutdown();
|
||||
|
||||
SDL_DestroyRenderer(Renderer::Get().renderer());
|
||||
// Finally destroy window
|
||||
LOG_INFO("Window", "Destroying window...");
|
||||
SDL_DestroyWindow(window.window_.get());
|
||||
|
||||
LOG_INFO("Window", "Shutting down SDL...");
|
||||
SDL_Quit();
|
||||
|
||||
LOG_INFO("Window", "Shutdown complete");
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "util/sdl_deleter.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/backend/irenderer.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace core {
|
||||
@@ -20,136 +21,13 @@ struct Window {
|
||||
bool active_ = true;
|
||||
};
|
||||
|
||||
absl::Status CreateWindow(Window &window, int flags);
|
||||
// Legacy CreateWindow (deprecated - use Controller::OnEntry instead)
|
||||
// Kept for backward compatibility with test code
|
||||
absl::Status CreateWindow(Window& window, gfx::IRenderer* renderer = nullptr,
|
||||
int flags = SDL_WINDOW_RESIZABLE);
|
||||
absl::Status HandleEvents(Window &window);
|
||||
absl::Status ShutdownWindow(Window &window);
|
||||
|
||||
/**
|
||||
* @class Renderer
|
||||
* @brief The Renderer class represents the renderer for the Yaze application.
|
||||
*
|
||||
* This class is a singleton that provides functionality for creating and
|
||||
* rendering bitmaps to the screen. It also includes methods for updating
|
||||
* bitmaps on the screen.
|
||||
*
|
||||
* IMPORTANT: This class MUST be used only on the main thread because:
|
||||
* 1. SDL_Renderer operations are not thread-safe
|
||||
* 2. OpenGL/DirectX contexts are bound to the creating thread
|
||||
* 3. Texture creation and rendering must happen on the main UI thread
|
||||
*
|
||||
* For performance optimization during ROM loading:
|
||||
* - Use deferred texture creation (CreateBitmapWithoutTexture) for bulk operations
|
||||
* - Batch texture creation operations when possible
|
||||
* - Consider background processing of bitmap data before texture creation
|
||||
*/
|
||||
class Renderer {
|
||||
public:
|
||||
static Renderer &Get() {
|
||||
static Renderer instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the SDL renderer on the main thread
|
||||
*
|
||||
* This MUST be called from the main thread as SDL renderer operations
|
||||
* are not thread-safe and require the OpenGL/DirectX context to be bound
|
||||
* to the calling thread.
|
||||
*/
|
||||
absl::Status CreateRenderer(SDL_Window *window) {
|
||||
renderer_ = std::unique_ptr<SDL_Renderer, util::SDL_Deleter>(
|
||||
SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED));
|
||||
if (renderer_ == nullptr) {
|
||||
return absl::InternalError(
|
||||
absl::StrFormat("SDL_CreateRenderer: %s\n", SDL_GetError()));
|
||||
}
|
||||
SDL_SetRenderDrawBlendMode(renderer_.get(), SDL_BLENDMODE_BLEND);
|
||||
SDL_SetRenderDrawColor(renderer_.get(), 0x00, 0x00, 0x00, 0x00);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
auto renderer() -> SDL_Renderer * { return renderer_.get(); }
|
||||
|
||||
/**
|
||||
* @brief Create texture for bitmap on main thread
|
||||
*
|
||||
* This operation blocks the main thread and should be used sparingly
|
||||
* during bulk loading operations. Consider using CreateBitmapWithoutTexture
|
||||
* followed by batch texture creation.
|
||||
*/
|
||||
void RenderBitmap(gfx::Bitmap *bitmap) {
|
||||
bitmap->CreateTexture(renderer_.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update existing texture on main thread
|
||||
*/
|
||||
void UpdateBitmap(gfx::Bitmap *bitmap) {
|
||||
bitmap->UpdateTexture(renderer_.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create bitmap and immediately create texture (blocking operation)
|
||||
*
|
||||
* This is the original method that blocks during texture creation.
|
||||
* For performance during ROM loading, consider using CreateBitmapWithoutTexture
|
||||
* and deferring texture creation until needed.
|
||||
*/
|
||||
void CreateAndRenderBitmap(int width, int height, int depth,
|
||||
const std::vector<uint8_t> &data,
|
||||
gfx::Bitmap &bitmap, gfx::SnesPalette &palette) {
|
||||
bitmap.Create(width, height, depth, data);
|
||||
bitmap.SetPalette(palette);
|
||||
RenderBitmap(&bitmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create bitmap without creating texture (non-blocking)
|
||||
*
|
||||
* This method prepares the bitmap data and surface but doesn't create
|
||||
* the GPU texture, allowing for faster bulk operations during ROM loading.
|
||||
* Texture creation can be deferred until the bitmap is actually needed
|
||||
* for rendering.
|
||||
*/
|
||||
void CreateBitmapWithoutTexture(int width, int height, int depth,
|
||||
const std::vector<uint8_t> &data,
|
||||
gfx::Bitmap &bitmap, gfx::SnesPalette &palette) {
|
||||
bitmap.Create(width, height, depth, data);
|
||||
bitmap.SetPalette(palette);
|
||||
// Note: No RenderBitmap call - texture creation is deferred
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Batch create textures for multiple bitmaps
|
||||
*
|
||||
* This method can be used to efficiently create textures for multiple
|
||||
* bitmaps that have already been prepared with CreateBitmapWithoutTexture.
|
||||
* Useful for deferred texture creation during ROM loading.
|
||||
*/
|
||||
void BatchCreateTextures(std::vector<gfx::Bitmap*> &bitmaps) {
|
||||
for (auto* bitmap : bitmaps) {
|
||||
if (bitmap && !bitmap->texture()) {
|
||||
bitmap->CreateTexture(renderer_.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
SDL_SetRenderDrawColor(renderer_.get(), 0x00, 0x00, 0x00, 0x00);
|
||||
SDL_RenderClear(renderer_.get());
|
||||
}
|
||||
|
||||
void Present() { SDL_RenderPresent(renderer_.get()); }
|
||||
|
||||
private:
|
||||
Renderer() = default;
|
||||
|
||||
std::unique_ptr<SDL_Renderer, util::SDL_Deleter> renderer_;
|
||||
|
||||
Renderer(const Renderer &) = delete;
|
||||
Renderer &operator=(const Renderer &) = delete;
|
||||
};
|
||||
|
||||
} // namespace core
|
||||
} // namespace yaze
|
||||
#endif // YAZE_CORE_WINDOW_H_
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/time/clock.h"
|
||||
#include "app/core/platform/asset_loader.h"
|
||||
#include "app/platform/asset_loader.h"
|
||||
#include "app/editor/agent/agent_chat_widget.h"
|
||||
#include "app/editor/agent/agent_collaboration_coordinator.h"
|
||||
#include "app/editor/system/proposal_drawer.h"
|
||||
@@ -638,7 +638,7 @@ void AgentEditor::DrawPromptEditorPanel() {
|
||||
// Load prompt file if not initialized
|
||||
if (!prompt_editor_initialized_ && prompt_editor_) {
|
||||
std::string asset_path = "agent/" + active_prompt_file_;
|
||||
auto content_result = core::AssetLoader::LoadTextFile(asset_path);
|
||||
auto content_result = AssetLoader::LoadTextFile(asset_path);
|
||||
|
||||
if (content_result.ok()) {
|
||||
prompt_editor_->SetText(*content_result);
|
||||
@@ -923,7 +923,7 @@ void AgentEditor::DrawCommonTilesEditor() {
|
||||
|
||||
// Load/Save buttons
|
||||
if (ImGui::Button(ICON_MD_FOLDER_OPEN " Load", ImVec2(100, 0))) {
|
||||
auto content = core::AssetLoader::LoadTextFile("agent/common_tiles.txt");
|
||||
auto content = AssetLoader::LoadTextFile("agent/common_tiles.txt");
|
||||
if (content.ok()) {
|
||||
common_tiles_editor_->SetText(*content);
|
||||
common_tiles_initialized_ = true;
|
||||
@@ -954,7 +954,7 @@ void AgentEditor::DrawCommonTilesEditor() {
|
||||
|
||||
// Load if not initialized
|
||||
if (!common_tiles_initialized_ && common_tiles_editor_) {
|
||||
auto content = core::AssetLoader::LoadTextFile("agent/common_tiles.txt");
|
||||
auto content = AssetLoader::LoadTextFile("agent/common_tiles.txt");
|
||||
if (content.ok()) {
|
||||
common_tiles_editor_->SetText(*content);
|
||||
} else {
|
||||
@@ -1010,7 +1010,7 @@ void AgentEditor::DrawNewPromptCreator() {
|
||||
ImGui::Text("Start from template:");
|
||||
|
||||
if (ImGui::Button(ICON_MD_FILE_COPY " v1 (Basic)", ImVec2(-1, 0))) {
|
||||
auto content = core::AssetLoader::LoadTextFile("agent/system_prompt.txt");
|
||||
auto content = AssetLoader::LoadTextFile("agent/system_prompt.txt");
|
||||
if (content.ok() && prompt_editor_) {
|
||||
prompt_editor_->SetText(*content);
|
||||
if (toast_manager_) {
|
||||
@@ -1021,7 +1021,7 @@ void AgentEditor::DrawNewPromptCreator() {
|
||||
|
||||
if (ImGui::Button(ICON_MD_FILE_COPY " v2 (Enhanced)", ImVec2(-1, 0))) {
|
||||
auto content =
|
||||
core::AssetLoader::LoadTextFile("agent/system_prompt_v2.txt");
|
||||
AssetLoader::LoadTextFile("agent/system_prompt_v2.txt");
|
||||
if (content.ok() && prompt_editor_) {
|
||||
prompt_editor_->SetText(*content);
|
||||
if (toast_manager_) {
|
||||
@@ -1032,7 +1032,7 @@ void AgentEditor::DrawNewPromptCreator() {
|
||||
|
||||
if (ImGui::Button(ICON_MD_FILE_COPY " v3 (Proactive)", ImVec2(-1, 0))) {
|
||||
auto content =
|
||||
core::AssetLoader::LoadTextFile("agent/system_prompt_v3.txt");
|
||||
AssetLoader::LoadTextFile("agent/system_prompt_v3.txt");
|
||||
if (content.ok() && prompt_editor_) {
|
||||
prompt_editor_->SetText(*content);
|
||||
if (toast_manager_) {
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
#include "dungeon_canvas_viewer.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/core/window.h"
|
||||
#include "app/gfx/arena.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/rom.h"
|
||||
#include "util/log.h"
|
||||
#include "app/zelda3/dungeon/object_drawer.h"
|
||||
#include "app/zelda3/dungeon/object_renderer.h"
|
||||
#include "app/zelda3/dungeon/room.h"
|
||||
#include "app/zelda3/sprite/sprite.h"
|
||||
@@ -148,6 +144,7 @@ void DungeonCanvasViewer::DrawDungeonCanvas(int room_id) {
|
||||
object_interaction_.HandleCanvasMouseInput();
|
||||
object_interaction_.CheckForObjectSelection();
|
||||
object_interaction_.DrawSelectBox();
|
||||
object_interaction_.DrawSelectionHighlights(); // Draw selection highlights on top
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +192,9 @@ void DungeonCanvasViewer::RenderObjectInCanvas(const zelda3::RoomObject &object,
|
||||
// Ensure the bitmap is valid and has content
|
||||
if (object_bitmap.width() > 0 && object_bitmap.height() > 0) {
|
||||
object_bitmap.SetPalette(palette);
|
||||
core::Renderer::Get().RenderBitmap(&object_bitmap);
|
||||
// Queue texture creation for the object bitmap via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &object_bitmap);
|
||||
canvas_.DrawBitmap(object_bitmap, canvas_x, canvas_y, 1.0f, 255);
|
||||
return;
|
||||
}
|
||||
@@ -656,7 +655,10 @@ absl::Status DungeonCanvasViewer::UpdateRoomBackgroundLayers(int room_id) {
|
||||
if (current_palette_id_ < current_palette_group_.size()) {
|
||||
gfx::Arena::Get().gfx_sheets()[block].SetPaletteWithTransparent(
|
||||
current_palette_group_[current_palette_id_], 0);
|
||||
core::Renderer::Get().UpdateBitmap(&gfx::Arena::Get().gfx_sheets()[block]);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE,
|
||||
&gfx::Arena::Get().gfx_sheets()[block]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -671,7 +673,10 @@ absl::Status DungeonCanvasViewer::UpdateRoomBackgroundLayers(int room_id) {
|
||||
if (block >= 0 && block < gfx::Arena::Get().gfx_sheets().size()) {
|
||||
gfx::Arena::Get().gfx_sheets()[block].SetPaletteWithTransparent(
|
||||
sprites_aux1_pal_group[current_palette_id_], 0);
|
||||
core::Renderer::Get().UpdateBitmap(&gfx::Arena::Get().gfx_sheets()[block]);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE,
|
||||
&gfx::Arena::Get().gfx_sheets()[block]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -691,14 +696,9 @@ void DungeonCanvasViewer::RenderRoomBackgroundLayers(int room_id) {
|
||||
|
||||
if (bg1_bitmap.is_active() && bg1_bitmap.width() > 0 && bg1_bitmap.height() > 0) {
|
||||
if (!bg1_bitmap.texture()) {
|
||||
core::Renderer::Get().RenderBitmap(&bg1_bitmap);
|
||||
}
|
||||
|
||||
// DEBUG: Check SDL texture format
|
||||
Uint32 format;
|
||||
int access, w, h;
|
||||
if (SDL_QueryTexture(bg1_bitmap.texture(), &format, &access, &w, &h) == 0) {
|
||||
const char* format_name_cstr = SDL_GetPixelFormatName(format);
|
||||
// Queue texture creation for background layer 1 via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &bg1_bitmap);
|
||||
}
|
||||
|
||||
canvas_.DrawBitmap(bg1_bitmap, 0, 0, 1.0f, 255);
|
||||
@@ -706,7 +706,9 @@ void DungeonCanvasViewer::RenderRoomBackgroundLayers(int room_id) {
|
||||
|
||||
if (bg2_bitmap.is_active() && bg2_bitmap.width() > 0 && bg2_bitmap.height() > 0) {
|
||||
if (!bg2_bitmap.texture()) {
|
||||
core::Renderer::Get().RenderBitmap(&bg2_bitmap);
|
||||
// Queue texture creation for background layer 2 via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &bg2_bitmap);
|
||||
}
|
||||
canvas_.DrawBitmap(bg2_bitmap, 0, 0, 1.0f, 200);
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ class DungeonCanvasViewer {
|
||||
int current_active_room_tab_ = 0;
|
||||
|
||||
// Object interaction state
|
||||
bool object_interaction_enabled_ = false;
|
||||
bool object_interaction_enabled_ = true;
|
||||
|
||||
// Palette data
|
||||
uint64_t current_palette_group_id_ = 0;
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
|
||||
namespace yaze::editor {
|
||||
|
||||
using core::Renderer;
|
||||
|
||||
using ImGui::BeginTabBar;
|
||||
using ImGui::BeginTabItem;
|
||||
using ImGui::BeginTable;
|
||||
@@ -185,11 +183,15 @@ absl::Status DungeonEditor::Save() {
|
||||
}
|
||||
|
||||
absl::Status DungeonEditor::RefreshGraphics() {
|
||||
// Update graphics sheet textures via Arena's deferred texture queue
|
||||
std::for_each_n(
|
||||
rooms_[current_room_id_].blocks().begin(), 8, [this](int block) {
|
||||
gfx::Arena::Get().gfx_sheets()[block].SetPaletteWithTransparent(
|
||||
current_palette_group_[current_palette_id_], 0);
|
||||
Renderer::Get().UpdateBitmap(&gfx::Arena::Get().gfx_sheets()[block]);
|
||||
// Queue texture update for the modified graphics sheet
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE,
|
||||
&gfx::Arena::Get().gfx_sheets()[block]);
|
||||
});
|
||||
|
||||
auto sprites_aux1_pal_group = rom()->palette_group().sprites_aux1;
|
||||
@@ -198,7 +200,10 @@ absl::Status DungeonEditor::RefreshGraphics() {
|
||||
[this, &sprites_aux1_pal_group](int block) {
|
||||
gfx::Arena::Get().gfx_sheets()[block].SetPaletteWithTransparent(
|
||||
sprites_aux1_pal_group[current_palette_id_], 0);
|
||||
Renderer::Get().UpdateBitmap(&gfx::Arena::Get().gfx_sheets()[block]);
|
||||
// Queue texture update for the modified graphics sheet
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE,
|
||||
&gfx::Arena::Get().gfx_sheets()[block]);
|
||||
});
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
@@ -15,7 +15,9 @@ namespace yaze::editor {
|
||||
|
||||
// No table layout needed - all cards are independent
|
||||
|
||||
void DungeonEditorV2::Initialize() {
|
||||
void DungeonEditorV2::Initialize(gfx::IRenderer* renderer, Rom* rom) {
|
||||
renderer_ = renderer;
|
||||
rom_ = rom;
|
||||
// Don't initialize emulator preview yet - ROM might not be loaded
|
||||
// Will be initialized in Load() instead
|
||||
|
||||
@@ -24,6 +26,8 @@ void DungeonEditorV2::Initialize() {
|
||||
room_window_class_.DockingAllowUnclassed = false; // Room windows dock together
|
||||
}
|
||||
|
||||
void DungeonEditorV2::Initialize() {}
|
||||
|
||||
absl::Status DungeonEditorV2::Load() {
|
||||
if (!rom_ || !rom_->is_loaded()) {
|
||||
return absl::FailedPreconditionError("ROM not loaded");
|
||||
@@ -55,13 +59,13 @@ absl::Status DungeonEditorV2::Load() {
|
||||
object_selector_.set_rooms(&rooms_);
|
||||
|
||||
// NOW initialize emulator preview with loaded ROM
|
||||
object_emulator_preview_.Initialize(rom_);
|
||||
object_emulator_preview_.Initialize(renderer_, rom_);
|
||||
|
||||
// Initialize palette editor with loaded ROM
|
||||
palette_editor_.Initialize(rom_);
|
||||
|
||||
// Initialize unified object editor card
|
||||
object_editor_card_ = std::make_unique<ObjectEditorCard>(rom_, &canvas_viewer_);
|
||||
object_editor_card_ = std::make_unique<ObjectEditorCard>(renderer_, rom_, &canvas_viewer_);
|
||||
|
||||
// Wire palette changes to trigger room re-renders
|
||||
palette_editor_.SetOnPaletteChanged([this](int /*palette_id*/) {
|
||||
@@ -97,7 +101,8 @@ absl::Status DungeonEditorV2::Update() {
|
||||
"This parent window can be minimized or closed.");
|
||||
|
||||
// Render all independent cards (these create their own top-level windows)
|
||||
object_emulator_preview_.Render();
|
||||
// NOTE: Emulator preview is now integrated into ObjectEditorCard
|
||||
// object_emulator_preview_.Render(); // Removed - causing performance issues
|
||||
DrawLayout();
|
||||
|
||||
return absl::OkStatus();
|
||||
@@ -148,11 +153,7 @@ void DungeonEditorV2::DrawToolset() {
|
||||
|
||||
toolbar.AddSeparator();
|
||||
|
||||
if (toolbar.AddToggle(ICON_MD_CATEGORY, &show_object_selector_, "Toggle Object Selector (Legacy)")) {
|
||||
// Toggled
|
||||
}
|
||||
|
||||
if (toolbar.AddToggle(ICON_MD_CONSTRUCTION, &show_object_editor_, "Toggle Object Editor (Unified)")) {
|
||||
if (toolbar.AddToggle(ICON_MD_CONSTRUCTION, &show_object_editor_, "Toggle Object Editor")) {
|
||||
// Toggled
|
||||
}
|
||||
|
||||
@@ -186,18 +187,7 @@ void DungeonEditorV2::DrawLayout() {
|
||||
DrawRoomGraphicsCard();
|
||||
}
|
||||
|
||||
// 4. Legacy Object Selector Card (independent, dockable)
|
||||
if (show_object_selector_) {
|
||||
gui::EditorCard object_card(
|
||||
MakeCardTitle("Object Selector").c_str(),
|
||||
ICON_MD_CATEGORY, &show_object_selector_);
|
||||
if (object_card.Begin()) {
|
||||
object_selector_.Draw();
|
||||
}
|
||||
object_card.End();
|
||||
}
|
||||
|
||||
// 4b. Unified Object Editor Card (new, combines selector + preview + interaction)
|
||||
// 4. Unified Object Editor Card
|
||||
if (show_object_editor_ && object_editor_card_) {
|
||||
object_editor_card_->Draw(&show_object_editor_);
|
||||
}
|
||||
@@ -234,11 +224,16 @@ void DungeonEditorV2::DrawLayout() {
|
||||
room_cards_[room_id] = std::make_shared<gui::EditorCard>(
|
||||
card_name_str.c_str(), ICON_MD_GRID_ON, &open);
|
||||
room_cards_[room_id]->SetDefaultSize(700, 600);
|
||||
|
||||
// Set default position for first room to be docked with main window
|
||||
if (active_rooms_.Size == 1) {
|
||||
room_cards_[room_id]->SetPosition(gui::EditorCard::Position::Floating);
|
||||
}
|
||||
}
|
||||
|
||||
auto& room_card = room_cards_[room_id];
|
||||
|
||||
// Use docking class to make room cards dock together
|
||||
// CRITICAL: Use docking class BEFORE Begin() to make rooms tab together
|
||||
ImGui::SetNextWindowClass(&room_window_class_);
|
||||
|
||||
if (room_card->Begin(&open)) {
|
||||
@@ -500,15 +495,15 @@ void DungeonEditorV2::DrawRoomMatrixCard() {
|
||||
MakeCardTitle("Room Matrix").c_str(),
|
||||
ICON_MD_GRID_VIEW, &show_room_matrix_);
|
||||
|
||||
matrix_card.SetDefaultSize(520, 620);
|
||||
matrix_card.SetDefaultSize(440, 520);
|
||||
|
||||
if (matrix_card.Begin()) {
|
||||
// 16 wide x 19 tall = 304 cells (295 rooms + 9 empty)
|
||||
// 16 wide x 19 tall = 304 cells (296 rooms + 8 empty)
|
||||
constexpr int kRoomsPerRow = 16;
|
||||
constexpr int kRoomsPerCol = 19;
|
||||
constexpr int kTotalRooms = 0x128; // 296 rooms (0x00-0x127)
|
||||
constexpr float kRoomCellSize = 28.0f; // Compact cells
|
||||
constexpr float kCellSpacing = 2.0f;
|
||||
constexpr float kRoomCellSize = 24.0f; // Smaller cells like ZScream
|
||||
constexpr float kCellSpacing = 1.0f; // Tighter spacing
|
||||
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
|
||||
@@ -527,30 +522,34 @@ void DungeonEditorV2::DrawRoomMatrixCard() {
|
||||
cell_min.y + kRoomCellSize);
|
||||
|
||||
if (is_valid_room) {
|
||||
// ALWAYS use palette-based color (lazy load if needed)
|
||||
ImU32 bg_color = IM_COL32(60, 60, 70, 255); // Fallback
|
||||
// Use simple deterministic color based on room ID (no loading needed)
|
||||
ImU32 bg_color;
|
||||
|
||||
// Try to get palette color
|
||||
uint8_t palette = 0;
|
||||
if (room_id < static_cast<int>(rooms_.size())) {
|
||||
// Lazy load room if needed to get palette
|
||||
if (!rooms_[room_id].IsLoaded()) {
|
||||
auto status = room_loader_.LoadRoom(room_id, rooms_[room_id]);
|
||||
if (status.ok()) {
|
||||
palette = rooms_[room_id].palette;
|
||||
}
|
||||
} else {
|
||||
palette = rooms_[room_id].palette;
|
||||
}
|
||||
|
||||
// Create a color variation based on palette ID
|
||||
float r = 0.3f + (palette % 4) * 0.15f;
|
||||
float g = 0.3f + ((palette / 4) % 4) * 0.15f;
|
||||
float b = 0.4f + ((palette / 16) % 2) * 0.2f;
|
||||
bg_color = IM_COL32(
|
||||
static_cast<int>(r * 255),
|
||||
static_cast<int>(g * 255),
|
||||
static_cast<int>(b * 255), 255);
|
||||
// Generate color from room ID - much faster than loading
|
||||
int hue = (room_id * 37) % 360; // Distribute colors across spectrum
|
||||
int saturation = 40 + (room_id % 3) * 15;
|
||||
int brightness = 50 + (room_id % 5) * 10;
|
||||
|
||||
// Convert HSV to RGB
|
||||
float h = hue / 60.0f;
|
||||
float s = saturation / 100.0f;
|
||||
float v = brightness / 100.0f;
|
||||
|
||||
int i = static_cast<int>(h);
|
||||
float f = h - i;
|
||||
int p = static_cast<int>(v * (1 - s) * 255);
|
||||
int q = static_cast<int>(v * (1 - s * f) * 255);
|
||||
int t = static_cast<int>(v * (1 - s * (1 - f)) * 255);
|
||||
int val = static_cast<int>(v * 255);
|
||||
|
||||
switch (i % 6) {
|
||||
case 0: bg_color = IM_COL32(val, t, p, 255); break;
|
||||
case 1: bg_color = IM_COL32(q, val, p, 255); break;
|
||||
case 2: bg_color = IM_COL32(p, val, t, 255); break;
|
||||
case 3: bg_color = IM_COL32(p, q, val, 255); break;
|
||||
case 4: bg_color = IM_COL32(t, p, val, 255); break;
|
||||
case 5: bg_color = IM_COL32(val, p, q, 255); break;
|
||||
default: bg_color = IM_COL32(60, 60, 70, 255); break;
|
||||
}
|
||||
|
||||
// Check if room is currently selected
|
||||
@@ -604,7 +603,7 @@ void DungeonEditorV2::DrawRoomMatrixCard() {
|
||||
OnRoomSelected(room_id);
|
||||
}
|
||||
|
||||
// Hover tooltip with room name
|
||||
// Hover tooltip with room name and status
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
if (room_id < static_cast<int>(std::size(zelda3::kRoomNames))) {
|
||||
@@ -612,9 +611,8 @@ void DungeonEditorV2::DrawRoomMatrixCard() {
|
||||
} else {
|
||||
ImGui::Text("Room %03X", room_id);
|
||||
}
|
||||
ImGui::Text("Palette: %02X",
|
||||
room_id < static_cast<int>(rooms_.size()) ?
|
||||
rooms_[room_id].palette : 0);
|
||||
ImGui::Text("Status: %s", is_open ? "Open" : is_current ? "Current" : "Closed");
|
||||
ImGui::Text("Click to %s", is_open ? "focus" : "open");
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -51,8 +51,9 @@ class DungeonEditorV2 : public Editor {
|
||||
}
|
||||
|
||||
// Editor interface
|
||||
void Initialize(gfx::IRenderer* renderer, Rom* rom);
|
||||
void Initialize() override;
|
||||
absl::Status Load() override;
|
||||
absl::Status Load();
|
||||
absl::Status Update() override;
|
||||
absl::Status Undo() override { return absl::UnimplementedError("Undo"); }
|
||||
absl::Status Redo() override { return absl::UnimplementedError("Redo"); }
|
||||
@@ -69,7 +70,7 @@ class DungeonEditorV2 : public Editor {
|
||||
room_selector_.set_rom(rom);
|
||||
canvas_viewer_.SetRom(rom);
|
||||
object_selector_.SetRom(rom);
|
||||
object_emulator_preview_.Initialize(rom);
|
||||
object_emulator_preview_.Initialize(renderer_, rom);
|
||||
}
|
||||
Rom* rom() const { return rom_; }
|
||||
|
||||
@@ -86,6 +87,7 @@ class DungeonEditorV2 : public Editor {
|
||||
}
|
||||
|
||||
private:
|
||||
gfx::IRenderer* renderer_ = nullptr;
|
||||
// Simple UI layout
|
||||
void DrawLayout();
|
||||
void DrawRoomTab(int room_id);
|
||||
@@ -117,8 +119,7 @@ class DungeonEditorV2 : public Editor {
|
||||
bool show_room_matrix_ = false;
|
||||
bool show_entrances_list_ = false;
|
||||
bool show_room_graphics_ = false; // Room graphics card
|
||||
bool show_object_selector_ = true; // Legacy object selector
|
||||
bool show_object_editor_ = true; // New unified object editor card
|
||||
bool show_object_editor_ = true; // Unified object editor card
|
||||
bool show_palette_editor_ = true;
|
||||
|
||||
// Palette management
|
||||
|
||||
@@ -150,25 +150,59 @@ void DungeonObjectInteraction::SelectObjectsInRect() {
|
||||
selected_object_indices_.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DungeonObjectInteraction::DrawSelectionHighlights() {
|
||||
if (!rooms_ || current_room_id_ < 0 || current_room_id_ >= 296) return;
|
||||
|
||||
// Highlight selected objects
|
||||
if (!selected_object_indices_.empty()) {
|
||||
for (size_t index : selected_object_indices_) {
|
||||
if (index < objects.size()) {
|
||||
const auto& object = objects[index];
|
||||
auto [canvas_x, canvas_y] =
|
||||
RoomToCanvasCoordinates(object.x_, object.y_);
|
||||
|
||||
// Draw selection highlight
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
ImVec2 canvas_pos = canvas_->zero_point();
|
||||
ImVec2 obj_start(canvas_pos.x + canvas_x - 2,
|
||||
canvas_pos.y + canvas_y - 2);
|
||||
ImVec2 obj_end(canvas_pos.x + canvas_x + 18,
|
||||
canvas_pos.y + canvas_y + 18);
|
||||
draw_list->AddRect(obj_start, obj_end, IM_COL32(0, 255, 255, 255), 0.0f,
|
||||
0, 2.0f);
|
||||
}
|
||||
auto& room = (*rooms_)[current_room_id_];
|
||||
const auto& objects = room.GetTileObjects();
|
||||
|
||||
// Draw highlights for all selected objects
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
ImVec2 canvas_pos = canvas_->zero_point();
|
||||
|
||||
for (size_t index : selected_object_indices_) {
|
||||
if (index < objects.size()) {
|
||||
const auto& object = objects[index];
|
||||
auto [canvas_x, canvas_y] = RoomToCanvasCoordinates(object.x_, object.y_);
|
||||
|
||||
// Calculate object size for highlight
|
||||
int obj_width = 8 + (object.size_ & 0x0F) * 4;
|
||||
int obj_height = 8 + ((object.size_ >> 4) & 0x0F) * 4;
|
||||
obj_width = std::min(obj_width, 64);
|
||||
obj_height = std::min(obj_height, 64);
|
||||
|
||||
// Draw cyan selection highlight
|
||||
ImVec2 obj_start(canvas_pos.x + canvas_x - 2,
|
||||
canvas_pos.y + canvas_y - 2);
|
||||
ImVec2 obj_end(canvas_pos.x + canvas_x + obj_width + 2,
|
||||
canvas_pos.y + canvas_y + obj_height + 2);
|
||||
|
||||
// Animated selection (pulsing effect)
|
||||
float pulse = 0.7f + 0.3f * std::sin(static_cast<float>(ImGui::GetTime()) * 4.0f);
|
||||
draw_list->AddRect(obj_start, obj_end,
|
||||
IM_COL32(0, static_cast<int>(255 * pulse), 255, 255),
|
||||
0.0f, 0, 2.5f);
|
||||
|
||||
// Draw corner handles for selected objects
|
||||
constexpr float handle_size = 4.0f;
|
||||
draw_list->AddRectFilled(
|
||||
ImVec2(obj_start.x - handle_size/2, obj_start.y - handle_size/2),
|
||||
ImVec2(obj_start.x + handle_size/2, obj_start.y + handle_size/2),
|
||||
IM_COL32(0, 255, 255, 255));
|
||||
draw_list->AddRectFilled(
|
||||
ImVec2(obj_end.x - handle_size/2, obj_start.y - handle_size/2),
|
||||
ImVec2(obj_end.x + handle_size/2, obj_start.y + handle_size/2),
|
||||
IM_COL32(0, 255, 255, 255));
|
||||
draw_list->AddRectFilled(
|
||||
ImVec2(obj_start.x - handle_size/2, obj_end.y - handle_size/2),
|
||||
ImVec2(obj_start.x + handle_size/2, obj_end.y + handle_size/2),
|
||||
IM_COL32(0, 255, 255, 255));
|
||||
draw_list->AddRectFilled(
|
||||
ImVec2(obj_end.x - handle_size/2, obj_end.y - handle_size/2),
|
||||
ImVec2(obj_end.x + handle_size/2, obj_end.y + handle_size/2),
|
||||
IM_COL32(0, 255, 255, 255));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ class DungeonObjectInteraction {
|
||||
// Selection rectangle (like OverworldEditor)
|
||||
void DrawObjectSelectRect();
|
||||
void SelectObjectsInRect();
|
||||
void DrawSelectionHighlights(); // Draw highlights for selected objects
|
||||
|
||||
// Drag and select box functionality
|
||||
void DrawSelectBox();
|
||||
|
||||
@@ -90,7 +90,9 @@ void DungeonObjectSelector::DrawObjectRenderer() {
|
||||
auto preview_bitmap = std::move(preview_result.value());
|
||||
if (preview_bitmap.width() > 0 && preview_bitmap.height() > 0) {
|
||||
preview_bitmap.SetPalette(preview_palette_);
|
||||
core::Renderer::Get().RenderBitmap(&preview_bitmap);
|
||||
// Queue texture creation via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &preview_bitmap);
|
||||
object_canvas_.DrawBitmap(preview_bitmap, preview_x, preview_y, 1.0f, 255);
|
||||
} else {
|
||||
// Fallback: Draw primitive shape
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
#include "dungeon_renderer.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/core/window.h"
|
||||
#include "app/gfx/arena.h"
|
||||
#include "app/gui/color.h"
|
||||
|
||||
namespace yaze::editor {
|
||||
|
||||
using core::Renderer;
|
||||
|
||||
void DungeonRenderer::RenderObjectInCanvas(const zelda3::RoomObject& object,
|
||||
const gfx::SnesPalette& palette) {
|
||||
// Validate ROM is loaded
|
||||
@@ -55,7 +51,9 @@ void DungeonRenderer::RenderObjectInCanvas(const zelda3::RoomObject& object,
|
||||
if (object_bitmap.width() > 0 && object_bitmap.height() > 0 &&
|
||||
object_bitmap.data() != nullptr) {
|
||||
object_bitmap.SetPalette(palette);
|
||||
core::Renderer::Get().RenderBitmap(&object_bitmap);
|
||||
// Queue texture creation for the object bitmap via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &object_bitmap);
|
||||
canvas_->DrawBitmap(object_bitmap, canvas_x, canvas_y, 1.0f, 255);
|
||||
// Cache the successfully rendered bitmap
|
||||
ObjectRenderCache cache_entry;
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
#include "object_editor_card.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/gfx/backend/irenderer.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/ui_helpers.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze::editor {
|
||||
|
||||
ObjectEditorCard::ObjectEditorCard(Rom* rom, DungeonCanvasViewer* canvas_viewer)
|
||||
: rom_(rom), canvas_viewer_(canvas_viewer), object_selector_(rom) {
|
||||
emulator_preview_.Initialize(rom);
|
||||
ObjectEditorCard::ObjectEditorCard(gfx::IRenderer* renderer, Rom* rom, DungeonCanvasViewer* canvas_viewer)
|
||||
: renderer_(renderer), rom_(rom), canvas_viewer_(canvas_viewer), object_selector_(rom) {
|
||||
emulator_preview_.Initialize(renderer, rom);
|
||||
}
|
||||
|
||||
void ObjectEditorCard::Draw(bool* p_open) {
|
||||
@@ -93,7 +94,7 @@ void ObjectEditorCard::DrawObjectSelector() {
|
||||
ImGui::Separator();
|
||||
|
||||
// Object list with categories
|
||||
if (ImGui::BeginChild("##ObjectList", ImVec2(0, -100), true)) {
|
||||
if (ImGui::BeginChild("##ObjectList", ImVec2(0, 0), true)) {
|
||||
// Floor objects
|
||||
if (ImGui::CollapsingHeader(ICON_MD_GRID_ON " Floor Objects",
|
||||
ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
@@ -268,8 +269,10 @@ void ObjectEditorCard::DrawObjectPreviewIcon(int object_id, const ImVec2& size)
|
||||
|
||||
void ObjectEditorCard::DrawSelectedObjectInfo() {
|
||||
ImGui::BeginGroup();
|
||||
|
||||
// Show current object for placement
|
||||
ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f),
|
||||
ICON_MD_INFO " Current Object:");
|
||||
ICON_MD_INFO " Current:");
|
||||
|
||||
if (has_preview_object_) {
|
||||
ImGui::SameLine();
|
||||
@@ -278,14 +281,35 @@ void ObjectEditorCard::DrawSelectedObjectInfo() {
|
||||
ImGui::Text("Layer: %s",
|
||||
preview_object_.layer_ == zelda3::RoomObject::BG1 ? "BG1" :
|
||||
preview_object_.layer_ == zelda3::RoomObject::BG2 ? "BG2" : "BG3");
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Mode: %s",
|
||||
interaction_mode_ == InteractionMode::Place ? "Place" :
|
||||
interaction_mode_ == InteractionMode::Select ? "Select" :
|
||||
interaction_mode_ == InteractionMode::Delete ? "Delete" : "None");
|
||||
} else {
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled("None selected");
|
||||
ImGui::TextDisabled("None");
|
||||
}
|
||||
|
||||
// Show selection count
|
||||
auto& interaction = canvas_viewer_->object_interaction();
|
||||
const auto& selected = interaction.GetSelectedObjectIndices();
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("|");
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.4f, 1.0f),
|
||||
ICON_MD_CHECKLIST " Selected: %zu", selected.size());
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("|");
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Mode: %s",
|
||||
interaction_mode_ == InteractionMode::Place ? ICON_MD_ADD_BOX " Place" :
|
||||
interaction_mode_ == InteractionMode::Select ? ICON_MD_CHECK_BOX " Select" :
|
||||
interaction_mode_ == InteractionMode::Delete ? ICON_MD_DELETE " Delete" : "None");
|
||||
|
||||
// Show quick actions for selections
|
||||
if (!selected.empty()) {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton(ICON_MD_CLEAR " Clear")) {
|
||||
interaction.ClearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndGroup();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#include "app/editor/dungeon/dungeon_canvas_viewer.h"
|
||||
#include "app/gfx/backend/irenderer.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/editor/dungeon/dungeon_object_selector.h"
|
||||
#include "app/gui/editor_layout.h"
|
||||
@@ -27,7 +28,7 @@ namespace editor {
|
||||
*/
|
||||
class ObjectEditorCard {
|
||||
public:
|
||||
ObjectEditorCard(Rom* rom, DungeonCanvasViewer* canvas_viewer);
|
||||
ObjectEditorCard(gfx::IRenderer* renderer, Rom* rom, DungeonCanvasViewer* canvas_viewer);
|
||||
|
||||
// Main update function
|
||||
void Draw(bool* p_open);
|
||||
@@ -59,7 +60,7 @@ class ObjectEditorCard {
|
||||
|
||||
// UI state
|
||||
int selected_tab_ = 0;
|
||||
bool show_emulator_preview_ = true;
|
||||
bool show_emulator_preview_ = false; // Disabled by default for performance
|
||||
bool show_object_list_ = true;
|
||||
bool show_interaction_controls_ = true;
|
||||
|
||||
@@ -75,6 +76,7 @@ class ObjectEditorCard {
|
||||
// Selected object for placement
|
||||
zelda3::RoomObject preview_object_{0, 0, 0, 0, 0};
|
||||
bool has_preview_object_ = false;
|
||||
gfx::IRenderer* renderer_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
|
||||
@@ -230,7 +230,9 @@ constexpr const char* kAssemblyEditorName = ICON_MD_CODE " Assembly Editor";
|
||||
constexpr const char* kDungeonEditorName = ICON_MD_CASTLE " Dungeon Editor";
|
||||
constexpr const char* kMusicEditorName = ICON_MD_MUSIC_NOTE " Music Editor";
|
||||
|
||||
void EditorManager::Initialize(const std::string& filename) {
|
||||
void EditorManager::Initialize(gfx::IRenderer* renderer, const std::string& filename) {
|
||||
renderer_ = renderer;
|
||||
|
||||
// Point to a blank editor set when no ROM is loaded
|
||||
current_editor_set_ = &blank_editor_set_;
|
||||
|
||||
@@ -2097,6 +2099,8 @@ absl::Status EditorManager::LoadAssets() {
|
||||
|
||||
current_editor_set_->overworld_editor_.Initialize();
|
||||
current_editor_set_->message_editor_.Initialize();
|
||||
// Initialize the dungeon editor with the renderer
|
||||
current_editor_set_->dungeon_editor_.Initialize(renderer_, current_rom_);
|
||||
ASSIGN_OR_RETURN(*gfx::Arena::Get().mutable_gfx_sheets(),
|
||||
LoadAllGraphicsData(*current_rom_));
|
||||
RETURN_IF_ERROR(current_editor_set_->overworld_editor_.Load());
|
||||
|
||||
@@ -105,7 +105,7 @@ class EditorManager {
|
||||
context_.popup_manager = popup_manager_.get();
|
||||
}
|
||||
|
||||
void Initialize(const std::string& filename = "");
|
||||
void Initialize(gfx::IRenderer* renderer, const std::string& filename = "");
|
||||
absl::Status Update();
|
||||
void DrawMenuBar();
|
||||
|
||||
@@ -262,6 +262,8 @@ class EditorManager {
|
||||
EditorSet* current_editor_set_ = nullptr;
|
||||
Editor* current_editor_ = nullptr;
|
||||
EditorSet blank_editor_set_{};
|
||||
|
||||
gfx::IRenderer* renderer_ = nullptr;
|
||||
|
||||
core::YazeProject current_project_;
|
||||
EditorContext context_;
|
||||
|
||||
@@ -29,8 +29,6 @@
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
|
||||
using core::Renderer;
|
||||
|
||||
using gfx::kPaletteGroupAddressesKeys;
|
||||
using ImGui::Button;
|
||||
using ImGui::InputInt;
|
||||
@@ -45,7 +43,50 @@ constexpr ImGuiTableFlags kGfxEditTableFlags =
|
||||
|
||||
void GraphicsEditor::Initialize() {}
|
||||
|
||||
absl::Status GraphicsEditor::Load() { return absl::OkStatus(); }
|
||||
absl::Status GraphicsEditor::Load() {
|
||||
gfx::ScopedTimer timer("GraphicsEditor::Load");
|
||||
|
||||
// Initialize all graphics sheets with appropriate palettes from ROM
|
||||
if (rom()->is_loaded()) {
|
||||
auto& sheets = gfx::Arena::Get().gfx_sheets();
|
||||
|
||||
// Apply default palettes to all sheets based on common SNES ROM structure
|
||||
// Sheets 0-112: Use overworld/dungeon palettes
|
||||
// Sheets 113-127: Use sprite palettes
|
||||
// Sheets 128-222: Use auxiliary/menu palettes
|
||||
|
||||
for (int i = 0; i < kNumGfxSheets; i++) {
|
||||
if (sheets[i].is_active() && sheets[i].surface()) {
|
||||
// Determine which palette group to use based on sheet index
|
||||
gfx::SnesPalette sheet_palette;
|
||||
|
||||
if (i < 113) {
|
||||
// Overworld/Dungeon graphics - use main palette
|
||||
auto palette_group = rom()->palette_group().dungeon_main;
|
||||
sheet_palette = palette_group[0]; // Use first palette as default
|
||||
} else if (i < 128) {
|
||||
// Sprite graphics - use sprite palettes
|
||||
auto palette_group = rom()->palette_group().sprites_aux1;
|
||||
sheet_palette = palette_group[0];
|
||||
} else {
|
||||
// Auxiliary graphics - use HUD/menu palettes
|
||||
sheet_palette = rom()->palette_group().hud.palette(0);
|
||||
}
|
||||
|
||||
// Apply palette and queue texture creation
|
||||
sheets[i].SetPalette(sheet_palette);
|
||||
|
||||
// Queue texture creation if not already created
|
||||
if (!sheets[i].texture()) {
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &sheets[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status GraphicsEditor::Update() {
|
||||
DrawToolset();
|
||||
@@ -273,36 +314,43 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() {
|
||||
graphics_bin_canvas_.DrawBackground(ImVec2(0x100 + 1, 0x40 + 1));
|
||||
graphics_bin_canvas_.DrawContextMenu();
|
||||
if (value.is_active()) {
|
||||
auto texture = value.texture();
|
||||
graphics_bin_canvas_.draw_list()->AddImage(
|
||||
(ImTextureID)(intptr_t)texture,
|
||||
ImVec2(graphics_bin_canvas_.zero_point().x + 2,
|
||||
graphics_bin_canvas_.zero_point().y + 2),
|
||||
ImVec2(graphics_bin_canvas_.zero_point().x +
|
||||
value.width() * sheet_scale_,
|
||||
graphics_bin_canvas_.zero_point().y +
|
||||
value.height() * sheet_scale_));
|
||||
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
|
||||
current_sheet_ = key;
|
||||
open_sheets_.insert(key);
|
||||
// Ensure texture exists for active sheets
|
||||
if (!value.texture() && value.surface()) {
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &value);
|
||||
}
|
||||
|
||||
auto texture = value.texture();
|
||||
if (texture) {
|
||||
graphics_bin_canvas_.draw_list()->AddImage(
|
||||
(ImTextureID)(intptr_t)texture,
|
||||
ImVec2(graphics_bin_canvas_.zero_point().x + 2,
|
||||
graphics_bin_canvas_.zero_point().y + 2),
|
||||
ImVec2(graphics_bin_canvas_.zero_point().x +
|
||||
value.width() * sheet_scale_,
|
||||
graphics_bin_canvas_.zero_point().y +
|
||||
value.height() * sheet_scale_));
|
||||
|
||||
// Add a slightly transparent rectangle behind the text
|
||||
ImVec2 text_pos(graphics_bin_canvas_.zero_point().x + 2,
|
||||
graphics_bin_canvas_.zero_point().y + 2);
|
||||
ImVec2 text_size =
|
||||
ImGui::CalcTextSize(absl::StrFormat("%02X", key).c_str());
|
||||
ImVec2 rent_min(text_pos.x, text_pos.y);
|
||||
ImVec2 rent_max(text_pos.x + text_size.x, text_pos.y + text_size.y);
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
|
||||
current_sheet_ = key;
|
||||
open_sheets_.insert(key);
|
||||
}
|
||||
|
||||
graphics_bin_canvas_.draw_list()->AddRectFilled(rent_min, rent_max,
|
||||
IM_COL32(0, 125, 0, 128));
|
||||
// Add a slightly transparent rectangle behind the text
|
||||
ImVec2 text_pos(graphics_bin_canvas_.zero_point().x + 2,
|
||||
graphics_bin_canvas_.zero_point().y + 2);
|
||||
ImVec2 text_size =
|
||||
ImGui::CalcTextSize(absl::StrFormat("%02X", key).c_str());
|
||||
ImVec2 rect_min(text_pos.x, text_pos.y);
|
||||
ImVec2 rect_max(text_pos.x + text_size.x, text_pos.y + text_size.y);
|
||||
|
||||
graphics_bin_canvas_.draw_list()->AddText(
|
||||
text_pos, IM_COL32(125, 255, 125, 255),
|
||||
absl::StrFormat("%02X", key).c_str());
|
||||
graphics_bin_canvas_.draw_list()->AddRectFilled(rect_min, rect_max,
|
||||
IM_COL32(0, 125, 0, 128));
|
||||
|
||||
graphics_bin_canvas_.draw_list()->AddText(
|
||||
text_pos, IM_COL32(125, 255, 125, 255),
|
||||
absl::StrFormat("%02X", key).c_str());
|
||||
}
|
||||
key++;
|
||||
}
|
||||
graphics_bin_canvas_.DrawGrid(16.0f);
|
||||
@@ -362,13 +410,19 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
|
||||
current_sheet_canvas_.DrawTileOnBitmap(tile_size_, ¤t_bitmap,
|
||||
current_color_);
|
||||
// Use batch operations for texture updates
|
||||
current_bitmap.QueueTextureUpdate(core::Renderer::Get().renderer());
|
||||
// current_bitmap.QueueTextureUpdate(core::Renderer::Get().renderer());
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, ¤t_bitmap);
|
||||
};
|
||||
|
||||
current_sheet_canvas_.UpdateColorPainter(
|
||||
gfx::Arena::Get().mutable_gfx_sheets()->at(sheet_id),
|
||||
nullptr, gfx::Arena::Get().mutable_gfx_sheets()->at(sheet_id),
|
||||
current_color_, draw_tile_event, tile_size_, current_scale_);
|
||||
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE,
|
||||
&gfx::Arena::Get().mutable_gfx_sheets()->at(sheet_id));
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
@@ -399,7 +453,7 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
|
||||
current_sheet_ = id;
|
||||
// ImVec2(0x100, 0x40),
|
||||
current_sheet_canvas_.UpdateColorPainter(
|
||||
gfx::Arena::Get().mutable_gfx_sheets()->at(id), current_color_,
|
||||
nullptr, gfx::Arena::Get().mutable_gfx_sheets()->at(id), current_color_,
|
||||
[&]() {
|
||||
|
||||
},
|
||||
@@ -415,9 +469,6 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
|
||||
}
|
||||
}
|
||||
|
||||
// Process all queued texture updates at once
|
||||
gfx::Arena::Get().ProcessBatchTextureUpdates();
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
@@ -426,24 +477,73 @@ absl::Status GraphicsEditor::UpdatePaletteColumn() {
|
||||
auto palette_group = *rom()->palette_group().get_group(
|
||||
kPaletteGroupAddressesKeys[edit_palette_group_name_index_]);
|
||||
auto palette = palette_group.palette(edit_palette_index_);
|
||||
gui::TextWithSeparators("ROM Palette");
|
||||
ImGui::SetNextItemWidth(100.f);
|
||||
gui::TextWithSeparators("ROM Palette Management");
|
||||
|
||||
// Quick palette presets for common SNES graphics types
|
||||
ImGui::Text("Quick Presets:");
|
||||
if (ImGui::Button("Overworld")) {
|
||||
edit_palette_group_name_index_ = 0; // Dungeon Main
|
||||
edit_palette_index_ = 0;
|
||||
refresh_graphics_ = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Dungeon")) {
|
||||
edit_palette_group_name_index_ = 0; // Dungeon Main
|
||||
edit_palette_index_ = 1;
|
||||
refresh_graphics_ = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Sprites")) {
|
||||
edit_palette_group_name_index_ = 4; // Sprites Aux1
|
||||
edit_palette_index_ = 0;
|
||||
refresh_graphics_ = true;
|
||||
}
|
||||
ImGui::Separator();
|
||||
|
||||
// Apply current palette to current sheet
|
||||
if (ImGui::Button("Apply to Current Sheet") && !open_sheets_.empty()) {
|
||||
refresh_graphics_ = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Apply to All Sheets")) {
|
||||
// Apply current palette to all active sheets
|
||||
for (int i = 0; i < kNumGfxSheets; i++) {
|
||||
auto& sheet = gfx::Arena::Get().mutable_gfx_sheets()->data()[i];
|
||||
if (sheet.is_active() && sheet.surface()) {
|
||||
sheet.SetPaletteWithTransparent(palette, edit_palette_sub_index_);
|
||||
if (sheet.texture()) {
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &sheet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::SetNextItemWidth(150.f);
|
||||
ImGui::Combo("Palette Group", (int*)&edit_palette_group_name_index_,
|
||||
kPaletteGroupAddressesKeys,
|
||||
IM_ARRAYSIZE(kPaletteGroupAddressesKeys));
|
||||
ImGui::SetNextItemWidth(100.f);
|
||||
gui::InputHex("Palette Group Index", &edit_palette_index_);
|
||||
gui::InputHex("Palette Index", &edit_palette_index_);
|
||||
ImGui::SetNextItemWidth(100.f);
|
||||
gui::InputHex("Sub-Palette", &edit_palette_sub_index_);
|
||||
|
||||
gui::SelectablePalettePipeline(edit_palette_sub_index_, refresh_graphics_,
|
||||
palette);
|
||||
|
||||
if (refresh_graphics_ && !open_sheets_.empty()) {
|
||||
gfx::Arena::Get()
|
||||
.mutable_gfx_sheets()
|
||||
->data()[current_sheet_]
|
||||
.SetPaletteWithTransparent(palette, edit_palette_sub_index_);
|
||||
Renderer::Get().UpdateBitmap(
|
||||
&gfx::Arena::Get().mutable_gfx_sheets()->data()[current_sheet_]);
|
||||
auto& current = gfx::Arena::Get().mutable_gfx_sheets()->data()[current_sheet_];
|
||||
if (current.is_active() && current.surface()) {
|
||||
current.SetPaletteWithTransparent(palette, edit_palette_sub_index_);
|
||||
if (current.texture()) {
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, ¤t);
|
||||
} else {
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, ¤t);
|
||||
}
|
||||
}
|
||||
refresh_graphics_ = false;
|
||||
}
|
||||
}
|
||||
@@ -583,7 +683,8 @@ absl::Status GraphicsEditor::DrawCgxImport() {
|
||||
cgx_bitmap_.Create(0x80, 0x200, 8, decoded_cgx_);
|
||||
if (col_file_) {
|
||||
cgx_bitmap_.SetPalette(decoded_col_);
|
||||
Renderer::Get().RenderBitmap(&cgx_bitmap_);
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &cgx_bitmap_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -613,7 +714,8 @@ absl::Status GraphicsEditor::DrawScrImport() {
|
||||
scr_bitmap_.Create(0x100, 0x100, 8, decoded_scr_data_);
|
||||
if (scr_loaded_) {
|
||||
scr_bitmap_.SetPalette(decoded_col_);
|
||||
Renderer::Get().RenderBitmap(&scr_bitmap_);
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &scr_bitmap_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -815,7 +917,8 @@ absl::Status GraphicsEditor::DecompressImportData(int size) {
|
||||
}
|
||||
}
|
||||
|
||||
Renderer::Get().RenderBitmap(&bin_bitmap_);
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &bin_bitmap_);
|
||||
gfx_loaded_ = true;
|
||||
|
||||
return absl::OkStatus();
|
||||
@@ -844,7 +947,8 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
|
||||
gfx_sheets_[i].SetPalette(z3_rom_palette_);
|
||||
}
|
||||
|
||||
Renderer::Get().RenderBitmap(&gfx_sheets_[i]);
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &gfx_sheets_[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -868,7 +972,8 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
|
||||
gfx_sheets_[i].SetPalette(z3_rom_palette_);
|
||||
}
|
||||
|
||||
Renderer::Get().RenderBitmap(&gfx_sheets_[i]);
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &gfx_sheets_[i]);
|
||||
i++;
|
||||
}
|
||||
super_donkey_ = true;
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
|
||||
using core::Renderer;
|
||||
|
||||
constexpr uint32_t kRedPen = 0xFF0000FF;
|
||||
|
||||
@@ -56,7 +55,9 @@ absl::Status ScreenEditor::Load() {
|
||||
sheets_[i].Get8x8Tile(tile_index, x, y, tile_data, tile_data_offset);
|
||||
tile8_individual_.emplace_back(gfx::Bitmap(8, 8, 4, tile_data));
|
||||
tile8_individual_.back().SetPalette(*rom()->mutable_dungeon_palette(3));
|
||||
Renderer::Get().RenderBitmap(&tile8_individual_.back());
|
||||
// Queue texture creation via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &tile8_individual_.back());
|
||||
}
|
||||
tile_data_offset = 0;
|
||||
}
|
||||
@@ -249,15 +250,17 @@ void ScreenEditor::DrawDungeonMapScreen(int i) {
|
||||
int tile16_id = tile_ids_to_render[idx];
|
||||
ImVec2 pos = tile_positions[idx];
|
||||
|
||||
gfx::RenderTile16(tile16_blockset_, tile16_id);
|
||||
gfx::RenderTile16(nullptr, tile16_blockset_, tile16_id);
|
||||
// Get tile from cache after rendering
|
||||
auto* cached_tile = tile16_blockset_.tile_cache.GetTile(tile16_id);
|
||||
if (cached_tile && cached_tile->is_active()) {
|
||||
// Ensure the cached tile has a valid texture
|
||||
if (!cached_tile->texture()) {
|
||||
core::Renderer::Get().RenderBitmap(cached_tile);
|
||||
// Queue texture creation via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, cached_tile);
|
||||
}
|
||||
screen_canvas_.DrawBitmap(*cached_tile, pos.x, pos.y, 4.0F);
|
||||
screen_canvas_.DrawBitmap(*cached_tile, pos.x, pos.y, 4.0F, 255);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,12 +385,12 @@ void ScreenEditor::DrawDungeonMapsRoomGfx() {
|
||||
(tilesheet_canvas_.points().front().y / 32) * 16;
|
||||
|
||||
// Render selected tile16 and cache tile metadata
|
||||
gfx::RenderTile16(tile16_blockset_, selected_tile16_);
|
||||
gfx::RenderTile16(nullptr, tile16_blockset_, selected_tile16_);
|
||||
std::ranges::copy(tile16_blockset_.tile_info[selected_tile16_],
|
||||
current_tile16_info.begin());
|
||||
}
|
||||
// Use direct bitmap rendering for tilesheet
|
||||
tilesheet_canvas_.DrawBitmap(tile16_blockset_.atlas, 1, 1, 2.0F);
|
||||
tilesheet_canvas_.DrawBitmap(tile16_blockset_.atlas, 1, 1, 2.0F, 255);
|
||||
tilesheet_canvas_.DrawGrid(32.f);
|
||||
tilesheet_canvas_.DrawOverlay();
|
||||
|
||||
@@ -408,16 +411,18 @@ void ScreenEditor::DrawDungeonMapsRoomGfx() {
|
||||
current_tile16_info[0], current_tile16_info[1],
|
||||
current_tile16_info[2], current_tile16_info[3], 212,
|
||||
selected_tile16_);
|
||||
gfx::UpdateTile16(tile16_blockset_, selected_tile16_);
|
||||
gfx::UpdateTile16(nullptr, tile16_blockset_, selected_tile16_);
|
||||
}
|
||||
// Get selected tile from cache
|
||||
auto* selected_tile = tile16_blockset_.tile_cache.GetTile(selected_tile16_);
|
||||
if (selected_tile && selected_tile->is_active()) {
|
||||
// Ensure the selected tile has a valid texture
|
||||
if (!selected_tile->texture()) {
|
||||
core::Renderer::Get().RenderBitmap(selected_tile);
|
||||
// Queue texture creation via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, selected_tile);
|
||||
}
|
||||
current_tile_canvas_.DrawBitmap(*selected_tile, 2, 4.0f);
|
||||
current_tile_canvas_.DrawBitmap(*selected_tile, 2, 2, 4.0f, 255);
|
||||
}
|
||||
current_tile_canvas_.DrawGrid(16.f);
|
||||
current_tile_canvas_.DrawOverlay();
|
||||
@@ -434,7 +439,7 @@ void ScreenEditor::DrawDungeonMapsRoomGfx() {
|
||||
current_tile16_info[0], current_tile16_info[1],
|
||||
current_tile16_info[2], current_tile16_info[3], 212,
|
||||
selected_tile16_);
|
||||
gfx::UpdateTile16(tile16_blockset_, selected_tile16_);
|
||||
gfx::UpdateTile16(nullptr, tile16_blockset_, selected_tile16_);
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
@@ -543,7 +548,9 @@ void ScreenEditor::LoadBinaryGfx() {
|
||||
converted_bin.begin() + ((i + 1) * 0x1000));
|
||||
sheets_.emplace(i, gfx::Bitmap(128, 32, 8, gfx_sheets[i]));
|
||||
sheets_[i].SetPalette(*rom()->mutable_dungeon_palette(3));
|
||||
Renderer::Get().RenderBitmap(&sheets_[i]);
|
||||
// Queue texture creation via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &sheets_[i]);
|
||||
}
|
||||
binary_gfx_loaded_ = true;
|
||||
} else {
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/gfx/arena.h"
|
||||
#include "app/gfx/performance_profiler.h"
|
||||
#include "util/file_util.h"
|
||||
#include "app/core/window.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
@@ -38,7 +38,6 @@ std::string DisplayTextOverflowError(int pos, bool bank) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
using core::Renderer;
|
||||
|
||||
using ImGui::BeginChild;
|
||||
using ImGui::BeginTable;
|
||||
@@ -74,11 +73,12 @@ void MessageEditor::Initialize() {
|
||||
}
|
||||
message_preview_.font_gfx16_data_ =
|
||||
gfx::SnesTo8bppSheet(raw_font_gfx_data_, /*bpp=*/2, /*num_sheets=*/2);
|
||||
Renderer::Get().CreateAndRenderBitmap(
|
||||
kFontGfxMessageSize, kFontGfxMessageSize, kFontGfxMessageDepth,
|
||||
message_preview_.font_gfx16_data_, font_gfx_bitmap_,
|
||||
font_preview_colors_);
|
||||
*font_gfx_bitmap_.mutable_palette() = font_preview_colors_;
|
||||
// Create bitmap and queue texture creation
|
||||
font_gfx_bitmap_.Create(kFontGfxMessageSize, kFontGfxMessageSize,
|
||||
kFontGfxMessageDepth, message_preview_.font_gfx16_data_);
|
||||
font_gfx_bitmap_.SetPalette(font_preview_colors_);
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &font_gfx_bitmap_);
|
||||
*current_font_gfx16_bitmap_.mutable_palette() = font_preview_colors_;
|
||||
|
||||
auto load_font = LoadFontGraphics(*rom());
|
||||
@@ -330,12 +330,16 @@ void MessageEditor::DrawMessagePreview() {
|
||||
if (current_font_gfx16_bitmap_.is_active()) {
|
||||
current_font_gfx16_bitmap_.mutable_data() =
|
||||
message_preview_.current_preview_data_;
|
||||
Renderer::Get().UpdateBitmap(¤t_font_gfx16_bitmap_);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, ¤t_font_gfx16_bitmap_);
|
||||
} else {
|
||||
Renderer::Get().CreateAndRenderBitmap(
|
||||
kCurrentMessageWidth, kCurrentMessageHeight, 172,
|
||||
message_preview_.current_preview_data_, current_font_gfx16_bitmap_,
|
||||
font_preview_colors_);
|
||||
// Create bitmap and queue texture creation
|
||||
current_font_gfx16_bitmap_.Create(kCurrentMessageWidth, kCurrentMessageHeight,
|
||||
172, message_preview_.current_preview_data_);
|
||||
current_font_gfx16_bitmap_.SetPalette(font_preview_colors_);
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, ¤t_font_gfx16_bitmap_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/core/asar_wrapper.h"
|
||||
#include "app/core/features.h"
|
||||
#include "app/core/window.h"
|
||||
#include "app/editor/overworld/map_properties.h"
|
||||
#include "app/editor/overworld/tile16_editor.h"
|
||||
#include "app/gfx/arena.h"
|
||||
@@ -43,7 +42,6 @@
|
||||
|
||||
namespace yaze::editor {
|
||||
|
||||
using core::Renderer;
|
||||
using namespace ImGui;
|
||||
|
||||
constexpr float kInputFieldSize = 30.f;
|
||||
@@ -710,7 +708,8 @@ void OverworldEditor::RenderUpdatedMapBitmap(
|
||||
current_bitmap.set_modified(true);
|
||||
|
||||
// Immediately update the texture to reflect changes
|
||||
core::Renderer::Get().UpdateBitmap(¤t_bitmap);
|
||||
// TODO: Queue texture for later rendering.
|
||||
// core::Renderer::Get().UpdateBitmap(¤t_bitmap);
|
||||
}
|
||||
|
||||
void OverworldEditor::CheckForOverworldEdits() {
|
||||
@@ -1092,11 +1091,13 @@ absl::Status OverworldEditor::CheckForCurrentMap() {
|
||||
|
||||
// Ensure tile16 blockset is fully updated before rendering
|
||||
if (tile16_blockset_.atlas.is_active()) {
|
||||
Renderer::Get().UpdateBitmap(&tile16_blockset_.atlas);
|
||||
// TODO: Queue texture for later rendering.
|
||||
// Renderer::Get().UpdateBitmap(&tile16_blockset_.atlas);
|
||||
}
|
||||
|
||||
// Update map texture with the traditional direct update approach
|
||||
Renderer::Get().UpdateBitmap(&maps_bmp_[current_map_]);
|
||||
// TODO: Queue texture for later rendering.
|
||||
// Renderer::Get().UpdateBitmap(&maps_bmp_[current_map_]);
|
||||
maps_bmp_[current_map_].set_modified(false);
|
||||
}
|
||||
|
||||
@@ -1411,9 +1412,10 @@ absl::Status OverworldEditor::DrawAreaGraphics() {
|
||||
overworld_.set_current_map(current_map_);
|
||||
palette_ = overworld_.current_area_palette();
|
||||
gfx::Bitmap bmp;
|
||||
Renderer::Get().CreateAndRenderBitmap(0x80, kOverworldMapSize, 0x08,
|
||||
overworld_.current_graphics(), bmp,
|
||||
palette_);
|
||||
// TODO: Queue texture for later rendering.
|
||||
// Renderer::Get().CreateAndRenderBitmap(0x80, kOverworldMapSize, 0x08,
|
||||
// overworld_.current_graphics(), bmp,
|
||||
// palette_);
|
||||
current_graphics_set_[current_map_] = bmp;
|
||||
}
|
||||
}
|
||||
@@ -1483,17 +1485,19 @@ absl::Status OverworldEditor::LoadGraphics() {
|
||||
// This avoids blocking the main thread with GPU texture creation
|
||||
{
|
||||
gfx::ScopedTimer gfx_timer("CreateBitmapWithoutTexture_Graphics");
|
||||
Renderer::Get().CreateBitmapWithoutTexture(0x80, kOverworldMapSize, 0x40,
|
||||
overworld_.current_graphics(),
|
||||
current_gfx_bmp_, palette_);
|
||||
// TODO: Queue texture for later rendering.
|
||||
// Renderer::Get().CreateBitmapWithoutTexture(0x80, kOverworldMapSize, 0x40,
|
||||
// overworld_.current_graphics(),
|
||||
// current_gfx_bmp_, palette_);
|
||||
}
|
||||
|
||||
LOG_DEBUG("OverworldEditor", "Loading overworld tileset (deferred textures).");
|
||||
{
|
||||
gfx::ScopedTimer tileset_timer("CreateBitmapWithoutTexture_Tileset");
|
||||
Renderer::Get().CreateBitmapWithoutTexture(
|
||||
0x80, 0x2000, 0x08, overworld_.tile16_blockset_data(),
|
||||
tile16_blockset_bmp_, palette_);
|
||||
// TODO: Queue texture for later rendering.
|
||||
// Renderer::Get().CreateBitmapWithoutTexture(
|
||||
// 0x80, 0x2000, 0x08, overworld_.tile16_blockset_data(),
|
||||
// tile16_blockset_bmp_, palette_);
|
||||
}
|
||||
map_blockset_loaded_ = true;
|
||||
|
||||
@@ -1504,8 +1508,14 @@ absl::Status OverworldEditor::LoadGraphics() {
|
||||
{
|
||||
gfx::ScopedTimer tilemap_timer("CreateTilemap");
|
||||
tile16_blockset_ =
|
||||
gfx::CreateTilemap(tile16_blockset_data, 0x80, 0x2000, kTile16Size,
|
||||
gfx::CreateTilemap(renderer_,tile16_blockset_data, 0x80, 0x2000, kTile16Size,
|
||||
zelda3::kNumTile16Individual, palette_);
|
||||
|
||||
// Queue texture creation for the tile16 blockset atlas
|
||||
if (tile16_blockset_.atlas.is_active() && tile16_blockset_.atlas.surface()) {
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &tile16_blockset_.atlas);
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 2: Create bitmaps only for essential maps initially
|
||||
@@ -1566,7 +1576,9 @@ absl::Status OverworldEditor::LoadGraphics() {
|
||||
{
|
||||
gfx::ScopedTimer initial_textures_timer("CreateInitialTextures");
|
||||
for (int i = 0; i < initial_texture_count; ++i) {
|
||||
Renderer::Get().RenderBitmap(maps_to_texture[i]);
|
||||
// Queue texture creation/update for initial maps via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, maps_to_texture[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1588,7 +1600,10 @@ absl::Status OverworldEditor::LoadGraphics() {
|
||||
priority = (map_world == current_world_) ? 5 : 15; // Current world = priority 5, others = 15
|
||||
}
|
||||
|
||||
gfx::Arena::Get().QueueDeferredTexture(maps_to_texture[i], priority);
|
||||
// Queue texture creation for remaining maps via Arena's deferred system
|
||||
// Note: Priority system to be implemented in future enhancement
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, maps_to_texture[i]);
|
||||
}
|
||||
|
||||
if (core::FeatureFlags::get().overworld.kDrawOverworldSprites) {
|
||||
@@ -1617,20 +1632,16 @@ absl::Status OverworldEditor::LoadSpriteGraphics() {
|
||||
sprite_previews_[sprite.id()].Create(width, height, depth,
|
||||
*sprite.preview_graphics());
|
||||
sprite_previews_[sprite.id()].SetPalette(palette_);
|
||||
Renderer::Get().RenderBitmap(&(sprite_previews_[sprite.id()]));
|
||||
// TODO: Queue texture for later rendering.
|
||||
// Renderer::Get().RenderBitmap(&(sprite_previews_[sprite.id()]));
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void OverworldEditor::ProcessDeferredTextures() {
|
||||
// Use Arena's centralized progressive loading system
|
||||
// This makes progressive loading available to all editors
|
||||
auto batch = gfx::Arena::Get().GetNextDeferredTextureBatch(4, 2);
|
||||
|
||||
for (auto* bitmap : batch) {
|
||||
if (bitmap && !bitmap->texture()) {
|
||||
Renderer::Get().RenderBitmap(bitmap);
|
||||
}
|
||||
// Process queued texture commands via Arena's deferred system
|
||||
if (renderer_) {
|
||||
gfx::Arena::Get().ProcessTextureQueue(renderer_);
|
||||
}
|
||||
|
||||
// Also process deferred map refreshes for modified maps
|
||||
@@ -1682,8 +1693,9 @@ void OverworldEditor::EnsureMapTexture(int map_index) {
|
||||
}
|
||||
|
||||
if (!bitmap.texture() && bitmap.is_active()) {
|
||||
Renderer::Get().RenderBitmap(&bitmap);
|
||||
// Note: Arena automatically removes from deferred queue when textures are created
|
||||
// Queue texture creation for this map
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1783,11 +1795,14 @@ void OverworldEditor::RefreshChildMapOnDemand(int map_index) {
|
||||
map_index);
|
||||
}
|
||||
|
||||
// CRITICAL FIX: Force COMPLETE texture recreation for immediate visibility
|
||||
// UpdateBitmap() was still deferred - we need to force a full re-render
|
||||
|
||||
// Always recreate the texture to ensure immediate GPU update
|
||||
Renderer::Get().RenderBitmap(&maps_bmp_[map_index]);
|
||||
// Queue texture update to ensure changes are visible
|
||||
if (maps_bmp_[map_index].texture()) {
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &maps_bmp_[map_index]);
|
||||
} else {
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &maps_bmp_[map_index]);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle multi-area maps (large, wide, tall) with safe coordination
|
||||
@@ -1933,9 +1948,10 @@ void OverworldEditor::RefreshMultiAreaMapsSafely(int map_index,
|
||||
}
|
||||
maps_bmp_[sibling].set_modified(false);
|
||||
|
||||
// Update texture if it exists
|
||||
// Queue texture update/creation
|
||||
if (maps_bmp_[sibling].texture()) {
|
||||
core::Renderer::Get().UpdateBitmap(&maps_bmp_[sibling]);
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &maps_bmp_[sibling]);
|
||||
} else {
|
||||
EnsureMapTexture(sibling);
|
||||
}
|
||||
@@ -2192,8 +2208,19 @@ absl::Status OverworldEditor::RefreshTile16Blockset() {
|
||||
|
||||
const auto tile16_data = overworld_.tile16_blockset_data();
|
||||
|
||||
gfx::UpdateTilemap(tile16_blockset_, tile16_data);
|
||||
gfx::UpdateTilemap(renderer_, tile16_blockset_, tile16_data);
|
||||
tile16_blockset_.atlas.SetPalette(palette_);
|
||||
|
||||
// Queue texture update for the atlas
|
||||
if (tile16_blockset_.atlas.texture() && tile16_blockset_.atlas.is_active()) {
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &tile16_blockset_.atlas);
|
||||
} else if (!tile16_blockset_.atlas.texture() && tile16_blockset_.atlas.is_active()) {
|
||||
// Create texture if it doesn't exist yet
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &tile16_blockset_.atlas);
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ class OverworldEditor : public Editor, public gfx::GfxContext {
|
||||
// MapPropertiesSystem will be initialized after maps_bmp_ and canvas are ready
|
||||
}
|
||||
|
||||
void Initialize(gfx::IRenderer* renderer, Rom* rom);
|
||||
void Initialize() override;
|
||||
absl::Status Load() override;
|
||||
absl::Status Update() final;
|
||||
@@ -326,6 +327,7 @@ class OverworldEditor : public Editor, public gfx::GfxContext {
|
||||
|
||||
Rom* rom_;
|
||||
|
||||
gfx::IRenderer* renderer_;
|
||||
Tile16Editor tile16_editor_{rom_, &tile16_blockset_};
|
||||
GfxGroupEditor gfx_group_editor_;
|
||||
PaletteEditor palette_editor_;
|
||||
|
||||
@@ -138,7 +138,9 @@ absl::Status OverworldEditor::DrawScratchSpace() {
|
||||
if (all_gfx_loaded_) {
|
||||
palette_ = overworld_.current_area_palette();
|
||||
current_slot.scratch_bitmap.SetPalette(palette_);
|
||||
core::Renderer::Get().RenderBitmap(¤t_slot.scratch_bitmap);
|
||||
// Queue texture creation via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, ¤t_slot.scratch_bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,9 +317,9 @@ void OverworldEditor::UpdateScratchBitmapTile(int tile_x, int tile_y,
|
||||
}
|
||||
|
||||
scratch_slot.scratch_bitmap.set_modified(true);
|
||||
// Use batch operations for texture updates
|
||||
scratch_slot.scratch_bitmap.QueueTextureUpdate(
|
||||
core::Renderer::Get().renderer());
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &scratch_slot.scratch_bitmap);
|
||||
scratch_slot.in_use = true;
|
||||
}
|
||||
|
||||
@@ -360,7 +362,9 @@ absl::Status OverworldEditor::SaveCurrentSelectionToScratch(int slot) {
|
||||
if (all_gfx_loaded_) {
|
||||
palette_ = overworld_.current_area_palette();
|
||||
scratch_spaces_[slot].scratch_bitmap.SetPalette(palette_);
|
||||
core::Renderer::Get().RenderBitmap(
|
||||
// Queue texture creation via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE,
|
||||
&scratch_spaces_[slot].scratch_bitmap);
|
||||
}
|
||||
|
||||
@@ -398,8 +402,6 @@ absl::Status OverworldEditor::SaveCurrentSelectionToScratch(int slot) {
|
||||
scratch_spaces_[slot].in_use = true;
|
||||
}
|
||||
|
||||
// Process all queued texture updates at once
|
||||
gfx::Arena::Get().ProcessBatchTextureUpdates();
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
@@ -433,7 +435,9 @@ absl::Status OverworldEditor::ClearScratchSpace(int slot) {
|
||||
auto& data = scratch_spaces_[slot].scratch_bitmap.mutable_data();
|
||||
std::fill(data.begin(), data.end(), 0);
|
||||
scratch_spaces_[slot].scratch_bitmap.set_modified(true);
|
||||
core::Renderer::Get().UpdateBitmap(&scratch_spaces_[slot].scratch_bitmap);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &scratch_spaces_[slot].scratch_bitmap);
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
#include <array>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/core/window.h"
|
||||
#include "app/gfx/arena.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/backend/irenderer.h"
|
||||
#include "app/gfx/performance_profiler.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/style.h"
|
||||
#include "app/rom.h"
|
||||
@@ -22,7 +21,6 @@
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
|
||||
using core::Renderer;
|
||||
using namespace ImGui;
|
||||
|
||||
absl::Status Tile16Editor::Initialize(
|
||||
@@ -34,14 +32,16 @@ absl::Status Tile16Editor::Initialize(
|
||||
current_gfx_bmp_.Create(current_gfx_bmp.width(), current_gfx_bmp.height(),
|
||||
current_gfx_bmp.depth(), current_gfx_bmp.vector());
|
||||
current_gfx_bmp_.SetPalette(current_gfx_bmp.palette()); // Temporary palette
|
||||
core::Renderer::Get().RenderBitmap(¤t_gfx_bmp_);
|
||||
// TODO: Queue texture for later rendering.
|
||||
// core::Renderer::Get().RenderBitmap(¤t_gfx_bmp_);
|
||||
|
||||
// Copy the tile16 blockset bitmap
|
||||
tile16_blockset_bmp_.Create(
|
||||
tile16_blockset_bmp.width(), tile16_blockset_bmp.height(),
|
||||
tile16_blockset_bmp.depth(), tile16_blockset_bmp.vector());
|
||||
tile16_blockset_bmp_.SetPalette(tile16_blockset_bmp.palette());
|
||||
core::Renderer::Get().RenderBitmap(&tile16_blockset_bmp_);
|
||||
// TODO: Queue texture for later rendering.
|
||||
// core::Renderer::Get().RenderBitmap(&tile16_blockset_bmp_);
|
||||
|
||||
// Note: LoadTile8() will be called after palette is set by overworld editor
|
||||
// This ensures proper palette coordination from the start
|
||||
@@ -50,7 +50,8 @@ absl::Status Tile16Editor::Initialize(
|
||||
current_tile16_bmp_.Create(kTile16Size, kTile16Size, 8,
|
||||
std::vector<uint8_t>(kTile16PixelCount, 0));
|
||||
current_tile16_bmp_.SetPalette(tile16_blockset_bmp.palette());
|
||||
core::Renderer::Get().RenderBitmap(¤t_tile16_bmp_);
|
||||
// TODO: Queue texture for later rendering.
|
||||
// core::Renderer::Get().RenderBitmap(¤t_tile16_bmp_);
|
||||
|
||||
// Initialize enhanced canvas features with proper sizing
|
||||
tile16_edit_canvas_.InitializeDefaults();
|
||||
@@ -345,8 +346,9 @@ absl::Status Tile16Editor::RefreshTile16Blockset() {
|
||||
// Mark atlas as modified to trigger regeneration
|
||||
tile16_blockset_->atlas.set_modified(true);
|
||||
|
||||
// Update the atlas bitmap using the safer direct approach
|
||||
core::Renderer::Get().UpdateBitmap(&tile16_blockset_->atlas);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &tile16_blockset_->atlas);
|
||||
|
||||
util::logf("Tile16 blockset refreshed and regenerated");
|
||||
return absl::OkStatus();
|
||||
@@ -390,9 +392,10 @@ absl::Status Tile16Editor::UpdateBlocksetBitmap() {
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the blockset bitmap as modified and use batch texture update
|
||||
// Mark the blockset bitmap as modified and queue texture update
|
||||
tile16_blockset_bmp_.set_modified(true);
|
||||
tile16_blockset_bmp_.QueueTextureUpdate(nullptr); // Use batch operations
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &tile16_blockset_bmp_);
|
||||
|
||||
// Also update the tile16 blockset atlas if available
|
||||
if (tile16_blockset_->atlas.is_active()) {
|
||||
@@ -415,11 +418,9 @@ absl::Status Tile16Editor::UpdateBlocksetBitmap() {
|
||||
}
|
||||
|
||||
tile16_blockset_->atlas.set_modified(true);
|
||||
tile16_blockset_->atlas.QueueTextureUpdate(nullptr);
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &tile16_blockset_->atlas);
|
||||
}
|
||||
|
||||
// Process all queued texture updates at once
|
||||
gfx::Arena::Get().ProcessBatchTextureUpdates();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
@@ -515,8 +516,9 @@ absl::Status Tile16Editor::RegenerateTile16BitmapFromROM() {
|
||||
}
|
||||
}
|
||||
|
||||
// Render the updated bitmap
|
||||
core::Renderer::Get().RenderBitmap(¤t_tile16_bmp_);
|
||||
// Queue texture creation via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, ¤t_tile16_bmp_);
|
||||
|
||||
util::logf("Regenerated Tile16 bitmap for tile %d from ROM data",
|
||||
current_tile16_);
|
||||
@@ -606,9 +608,10 @@ absl::Status Tile16Editor::DrawToCurrentTile16(ImVec2 pos,
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the bitmap as modified and update the renderer
|
||||
// Mark the bitmap as modified and queue texture update
|
||||
current_tile16_bmp_.set_modified(true);
|
||||
core::Renderer::Get().UpdateBitmap(¤t_tile16_bmp_);
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, ¤t_tile16_bmp_);
|
||||
|
||||
// Update ROM data when painting to tile16
|
||||
auto* tile_data = GetCurrentTile16Data();
|
||||
@@ -893,8 +896,9 @@ absl::Status Tile16Editor::UpdateTile16Edit() {
|
||||
}
|
||||
}
|
||||
|
||||
// Render the display tile
|
||||
core::Renderer::Get().RenderBitmap(&display_tile);
|
||||
// Queue texture creation for display tile
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &display_tile);
|
||||
|
||||
// CRITICAL FIX: Handle tile painting with simple click instead of click+drag
|
||||
// Draw the preview first
|
||||
@@ -1273,7 +1277,9 @@ absl::Status Tile16Editor::LoadTile8() {
|
||||
// Fallback to ROM palette
|
||||
tile_bitmap.SetPalette(rom()->palette_group().overworld_main[0]);
|
||||
}
|
||||
core::Renderer::Get().RenderBitmap(&tile_bitmap);
|
||||
// Queue texture creation via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &tile_bitmap);
|
||||
} catch (const std::exception& e) {
|
||||
util::logf("Error creating tile at (%d,%d): %s", tile_x, tile_y,
|
||||
e.what());
|
||||
@@ -1362,8 +1368,9 @@ absl::Status Tile16Editor::SetCurrentTile(int tile_id) {
|
||||
current_tile16_bmp_.SetPalette(rom()->palette_group().overworld_main[0]);
|
||||
}
|
||||
|
||||
// Render the bitmap
|
||||
core::Renderer::Get().RenderBitmap(¤t_tile16_bmp_);
|
||||
// Queue texture creation via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, ¤t_tile16_bmp_);
|
||||
|
||||
// Simple success logging
|
||||
util::logf("SetCurrentTile: loaded tile %d successfully", tile_id);
|
||||
@@ -1382,7 +1389,9 @@ absl::Status Tile16Editor::CopyTile16ToClipboard(int tile_id) {
|
||||
clipboard_tile16_.Create(16, 16, 8, tile_data);
|
||||
clipboard_tile16_.SetPalette(tile16_blockset_->atlas.palette());
|
||||
}
|
||||
core::Renderer::Get().RenderBitmap(&clipboard_tile16_);
|
||||
// Queue texture creation via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &clipboard_tile16_);
|
||||
|
||||
clipboard_has_data_ = true;
|
||||
return absl::OkStatus();
|
||||
@@ -1396,7 +1405,9 @@ absl::Status Tile16Editor::PasteTile16FromClipboard() {
|
||||
// Copy the clipboard data to the current tile16
|
||||
current_tile16_bmp_.Create(16, 16, 8, clipboard_tile16_.vector());
|
||||
current_tile16_bmp_.SetPalette(clipboard_tile16_.palette());
|
||||
core::Renderer::Get().RenderBitmap(¤t_tile16_bmp_);
|
||||
// Queue texture creation via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, ¤t_tile16_bmp_);
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
@@ -1409,7 +1420,9 @@ absl::Status Tile16Editor::SaveTile16ToScratchSpace(int slot) {
|
||||
// Create a copy of the current tile16 bitmap
|
||||
scratch_space_[slot].Create(16, 16, 8, current_tile16_bmp_.vector());
|
||||
scratch_space_[slot].SetPalette(current_tile16_bmp_.palette());
|
||||
core::Renderer::Get().RenderBitmap(&scratch_space_[slot]);
|
||||
// Queue texture creation via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &scratch_space_[slot]);
|
||||
|
||||
scratch_space_used_[slot] = true;
|
||||
return absl::OkStatus();
|
||||
@@ -1427,7 +1440,9 @@ absl::Status Tile16Editor::LoadTile16FromScratchSpace(int slot) {
|
||||
// Copy the scratch space data to the current tile16
|
||||
current_tile16_bmp_.Create(16, 16, 8, scratch_space_[slot].vector());
|
||||
current_tile16_bmp_.SetPalette(scratch_space_[slot].palette());
|
||||
core::Renderer::Get().RenderBitmap(¤t_tile16_bmp_);
|
||||
// Queue texture creation via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, ¤t_tile16_bmp_);
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
@@ -1471,7 +1486,9 @@ absl::Status Tile16Editor::FlipTile16Horizontal() {
|
||||
current_tile16_bmp_.SetPalette(palette_);
|
||||
current_tile16_bmp_.set_modified(true);
|
||||
|
||||
core::Renderer::Get().UpdateBitmap(¤t_tile16_bmp_);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, ¤t_tile16_bmp_);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
@@ -1504,7 +1521,9 @@ absl::Status Tile16Editor::FlipTile16Vertical() {
|
||||
current_tile16_bmp_.SetPalette(palette_);
|
||||
current_tile16_bmp_.set_modified(true);
|
||||
|
||||
core::Renderer::Get().UpdateBitmap(¤t_tile16_bmp_);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, ¤t_tile16_bmp_);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
@@ -1537,7 +1556,9 @@ absl::Status Tile16Editor::RotateTile16() {
|
||||
current_tile16_bmp_.SetPalette(palette_);
|
||||
current_tile16_bmp_.set_modified(true);
|
||||
|
||||
core::Renderer::Get().UpdateBitmap(¤t_tile16_bmp_);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, ¤t_tile16_bmp_);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
@@ -1574,7 +1595,9 @@ absl::Status Tile16Editor::FillTile16WithTile8(int tile8_id) {
|
||||
}
|
||||
|
||||
current_tile16_bmp_.set_modified(true);
|
||||
core::Renderer::Get().UpdateBitmap(¤t_tile16_bmp_);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, ¤t_tile16_bmp_);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
@@ -1590,7 +1613,9 @@ absl::Status Tile16Editor::ClearTile16() {
|
||||
std::fill(data.begin(), data.end(), 0);
|
||||
|
||||
current_tile16_bmp_.set_modified(true);
|
||||
core::Renderer::Get().UpdateBitmap(¤t_tile16_bmp_);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, ¤t_tile16_bmp_);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
@@ -1633,7 +1658,9 @@ absl::Status Tile16Editor::PreviewPaletteChange(uint8_t palette_id) {
|
||||
const auto& ow_main_pal_group = rom()->palette_group().overworld_main;
|
||||
if (ow_main_pal_group.size() > palette_id) {
|
||||
preview_tile16_.SetPaletteWithTransparent(ow_main_pal_group[0], palette_id);
|
||||
core::Renderer::Get().UpdateBitmap(&preview_tile16_);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &preview_tile16_);
|
||||
preview_dirty_ = true;
|
||||
}
|
||||
|
||||
@@ -1691,7 +1718,9 @@ absl::Status Tile16Editor::Undo() {
|
||||
y_flip = previous_state.y_flip;
|
||||
priority_tile = previous_state.priority;
|
||||
|
||||
core::Renderer::Get().UpdateBitmap(¤t_tile16_bmp_);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, ¤t_tile16_bmp_);
|
||||
undo_stack_.pop_back();
|
||||
|
||||
return absl::OkStatus();
|
||||
@@ -1714,7 +1743,9 @@ absl::Status Tile16Editor::Redo() {
|
||||
y_flip = next_state.y_flip;
|
||||
priority_tile = next_state.priority;
|
||||
|
||||
core::Renderer::Get().UpdateBitmap(¤t_tile16_bmp_);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, ¤t_tile16_bmp_);
|
||||
redo_stack_.pop_back();
|
||||
|
||||
return absl::OkStatus();
|
||||
@@ -1800,7 +1831,9 @@ absl::Status Tile16Editor::UpdateOverworldTilemap() {
|
||||
}
|
||||
|
||||
tile16_blockset_->atlas.set_modified(true);
|
||||
core::Renderer::Get().UpdateBitmap(&tile16_blockset_->atlas);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &tile16_blockset_->atlas);
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
@@ -1813,7 +1846,9 @@ absl::Status Tile16Editor::CommitChangesToBlockset() {
|
||||
|
||||
// Regenerate the tilemap data if needed
|
||||
if (tile16_blockset_->atlas.modified()) {
|
||||
core::Renderer::Get().UpdateBitmap(&tile16_blockset_->atlas);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &tile16_blockset_->atlas);
|
||||
}
|
||||
|
||||
// Update individual cached tiles
|
||||
@@ -1857,7 +1892,9 @@ absl::Status Tile16Editor::CommitChangesToOverworld() {
|
||||
}
|
||||
|
||||
tile16_blockset_->atlas.set_modified(true);
|
||||
core::Renderer::Get().UpdateBitmap(&tile16_blockset_->atlas);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &tile16_blockset_->atlas);
|
||||
}
|
||||
|
||||
// Step 4: Notify the parent editor (overworld editor) to regenerate its blockset
|
||||
@@ -2073,7 +2110,9 @@ absl::Status Tile16Editor::UpdateTile8Palette(int tile8_id) {
|
||||
// }
|
||||
|
||||
current_gfx_individual_[tile8_id].set_modified(true);
|
||||
Renderer::Get().UpdateBitmap(¤t_gfx_individual_[tile8_id]);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, ¤t_gfx_individual_[tile8_id]);
|
||||
|
||||
util::logf("Updated tile8 %d with palette slot %d (palette size: %zu colors)",
|
||||
tile8_id, current_palette_, display_palette.size());
|
||||
@@ -2125,7 +2164,9 @@ absl::Status Tile16Editor::RefreshAllPalettes() {
|
||||
// Apply the complete 256-color palette to the source bitmap (same as overworld)
|
||||
current_gfx_bmp_.SetPalette(display_palette);
|
||||
current_gfx_bmp_.set_modified(true);
|
||||
core::Renderer::Get().UpdateBitmap(¤t_gfx_bmp_);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, ¤t_gfx_bmp_);
|
||||
util::logf(
|
||||
"Applied complete 256-color palette to source bitmap (same as "
|
||||
"overworld)");
|
||||
@@ -2136,7 +2177,9 @@ absl::Status Tile16Editor::RefreshAllPalettes() {
|
||||
// Use complete 256-color palette (same as overworld system)
|
||||
current_tile16_bmp_.SetPalette(display_palette);
|
||||
current_tile16_bmp_.set_modified(true);
|
||||
core::Renderer::Get().UpdateBitmap(¤t_tile16_bmp_);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, ¤t_tile16_bmp_);
|
||||
}
|
||||
|
||||
// Update all individual tile8 graphics with complete 256-color palette
|
||||
@@ -2146,7 +2189,9 @@ absl::Status Tile16Editor::RefreshAllPalettes() {
|
||||
// The pixel data already contains correct color indices for the 256-color palette
|
||||
current_gfx_individual_[i].SetPalette(display_palette);
|
||||
current_gfx_individual_[i].set_modified(true);
|
||||
core::Renderer::Get().UpdateBitmap(¤t_gfx_individual_[i]);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, ¤t_gfx_individual_[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#if __APPLE__
|
||||
#include "app/core/platform/app_delegate.h"
|
||||
#include "app/platform/app_delegate.h"
|
||||
#endif
|
||||
|
||||
#include <SDL.h>
|
||||
@@ -14,6 +14,8 @@
|
||||
#include "absl/flags/parse.h"
|
||||
#include "app/emu/snes.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/gfx/backend/irenderer.h"
|
||||
#include "app/gfx/backend/sdl2_renderer.h"
|
||||
#include "util/sdl_deleter.h"
|
||||
|
||||
ABSL_FLAG(std::string, emu_rom, "", "Path to the ROM file to load.");
|
||||
@@ -86,17 +88,13 @@ int main(int argc, char **argv) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::unique_ptr<SDL_Renderer, SDL_Deleter> renderer_(
|
||||
SDL_CreateRenderer(window_.get(), -1,
|
||||
SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC),
|
||||
SDL_Deleter());
|
||||
if (!renderer_) {
|
||||
printf("SDL_CreateRenderer failed: %s\n", SDL_GetError());
|
||||
SDL_Quit();
|
||||
return EXIT_FAILURE;
|
||||
// Create and initialize the renderer
|
||||
auto renderer = std::make_unique<yaze::gfx::SDL2Renderer>();
|
||||
if (!renderer->Initialize(window_.get())) {
|
||||
printf("Failed to initialize renderer\n");
|
||||
SDL_Quit();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
SDL_SetRenderDrawBlendMode(renderer_.get(), SDL_BLENDMODE_BLEND);
|
||||
SDL_SetRenderDrawColor(renderer_.get(), 0x00, 0x00, 0x00, 0xFF);
|
||||
|
||||
// Initialize audio system
|
||||
constexpr int kAudioFrequency = 48000;
|
||||
@@ -120,10 +118,7 @@ int main(int argc, char **argv) {
|
||||
SDL_PauseAudioDevice(audio_device, 0);
|
||||
|
||||
// Create PPU texture for rendering
|
||||
SDL_Texture* ppu_texture = SDL_CreateTexture(renderer_.get(),
|
||||
SDL_PIXELFORMAT_RGBX8888,
|
||||
SDL_TEXTUREACCESS_STREAMING,
|
||||
512, 480);
|
||||
void* ppu_texture = renderer->CreateTexture(512, 480);
|
||||
if (!ppu_texture) {
|
||||
printf("SDL_CreateTexture failed: %s\n", SDL_GetError());
|
||||
SDL_CloseAudioDevice(audio_device);
|
||||
@@ -271,20 +266,17 @@ int main(int argc, char **argv) {
|
||||
// Render PPU output to texture
|
||||
void *ppu_pixels = nullptr;
|
||||
int ppu_pitch = 0;
|
||||
if (SDL_LockTexture(ppu_texture, nullptr, &ppu_pixels, &ppu_pitch) != 0) {
|
||||
printf("Failed to lock texture: %s\n", SDL_GetError());
|
||||
running = false;
|
||||
break;
|
||||
if (renderer->LockTexture(ppu_texture, nullptr, &ppu_pixels, &ppu_pitch)) {
|
||||
snes_.SetPixels(static_cast<uint8_t*>(ppu_pixels));
|
||||
renderer->UnlockTexture(ppu_texture);
|
||||
}
|
||||
snes_.SetPixels(static_cast<uint8_t*>(ppu_pixels));
|
||||
SDL_UnlockTexture(ppu_texture);
|
||||
}
|
||||
}
|
||||
|
||||
// Present rendered frame
|
||||
SDL_RenderClear(renderer_.get());
|
||||
SDL_RenderCopy(renderer_.get(), ppu_texture, nullptr, nullptr);
|
||||
SDL_RenderPresent(renderer_.get());
|
||||
renderer->Clear();
|
||||
renderer->RenderCopy(ppu_texture, nullptr, nullptr);
|
||||
renderer->Present();
|
||||
}
|
||||
|
||||
// === Cleanup SDL resources (in reverse order of initialization) ===
|
||||
@@ -292,7 +284,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
// Clean up texture
|
||||
if (ppu_texture) {
|
||||
SDL_DestroyTexture(ppu_texture);
|
||||
renderer->DestroyTexture(ppu_texture);
|
||||
ppu_texture = nullptr;
|
||||
}
|
||||
|
||||
@@ -302,7 +294,7 @@ int main(int argc, char **argv) {
|
||||
SDL_CloseAudioDevice(audio_device);
|
||||
|
||||
// Clean up renderer and window (done automatically by unique_ptr destructors)
|
||||
renderer_.reset();
|
||||
renderer->Shutdown();
|
||||
window_.reset();
|
||||
|
||||
// Quit SDL subsystems
|
||||
|
||||
@@ -5,7 +5,7 @@ if (NOT YAZE_MINIMAL_BUILD AND APPLE)
|
||||
MACOSX_BUNDLE
|
||||
app/main.cc
|
||||
app/rom.cc
|
||||
app/core/platform/app_delegate.mm
|
||||
app/platform/app_delegate.mm
|
||||
${YAZE_APP_EMU_SRC}
|
||||
${YAZE_APP_CORE_SRC}
|
||||
${YAZE_APP_EDITOR_SRC}
|
||||
|
||||
@@ -49,21 +49,24 @@ using ImGui::Separator;
|
||||
using ImGui::TableNextColumn;
|
||||
using ImGui::Text;
|
||||
|
||||
void Emulator::Initialize(gfx::IRenderer* renderer, const std::vector<uint8_t>& rom_data) {
|
||||
renderer_ = renderer;
|
||||
rom_data_ = rom_data;
|
||||
snes_.Init(rom_data_);
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
void Emulator::Run(Rom* rom) {
|
||||
static bool loaded = false;
|
||||
if (!snes_.running() && rom->is_loaded()) {
|
||||
// Use ARGB8888 format to match PPU output (XBGR layout with format=1)
|
||||
// Add better error handling and texture optimization
|
||||
ppu_texture_ = SDL_CreateTexture(core::Renderer::Get().renderer(),
|
||||
SDL_PIXELFORMAT_ARGB8888,
|
||||
SDL_TEXTUREACCESS_STREAMING, 512, 480);
|
||||
ppu_texture_ = renderer_->CreateTexture(512, 480);
|
||||
if (ppu_texture_ == NULL) {
|
||||
printf("Failed to create texture: %s\n", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
// Optimize texture for better performance
|
||||
SDL_SetTextureBlendMode(ppu_texture_, SDL_BLENDMODE_NONE);
|
||||
// renderer_->SetTextureBlendMode(ppu_texture_, SDL_BLENDMODE_NONE);
|
||||
rom_data_ = rom->vector();
|
||||
snes_.Init(rom_data_);
|
||||
|
||||
@@ -149,10 +152,9 @@ void Emulator::Run(Rom* rom) {
|
||||
// Update PPU texture only on rendered frames
|
||||
void* ppu_pixels_;
|
||||
int ppu_pitch_;
|
||||
if (SDL_LockTexture(ppu_texture_, NULL, &ppu_pixels_, &ppu_pitch_) ==
|
||||
0) {
|
||||
if (renderer_->LockTexture(ppu_texture_, NULL, &ppu_pixels_, &ppu_pitch_)) {
|
||||
snes_.SetPixels(static_cast<uint8_t*>(ppu_pixels_));
|
||||
SDL_UnlockTexture(ppu_texture_);
|
||||
renderer_->UnlockTexture(ppu_texture_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,11 @@
|
||||
|
||||
#include "app/emu/snes.h"
|
||||
#include "app/rom.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gfx {
|
||||
class IRenderer;
|
||||
} // namespace gfx
|
||||
|
||||
/**
|
||||
* @namespace yaze::emu
|
||||
@@ -39,6 +41,7 @@ class Emulator {
|
||||
public:
|
||||
Emulator() = default;
|
||||
~Emulator() = default;
|
||||
void Initialize(gfx::IRenderer* renderer, const std::vector<uint8_t>& rom_data);
|
||||
void Run(Rom* rom);
|
||||
|
||||
auto snes() -> Snes& { return snes_; }
|
||||
@@ -129,7 +132,9 @@ class Emulator {
|
||||
SDL_AudioDeviceID audio_device_;
|
||||
|
||||
Snes snes_;
|
||||
SDL_Texture* ppu_texture_;
|
||||
bool initialized_ = false;
|
||||
gfx::IRenderer* renderer_ = nullptr;
|
||||
void* ppu_texture_ = nullptr;
|
||||
|
||||
std::vector<uint8_t> rom_data_;
|
||||
|
||||
|
||||
@@ -3,11 +3,14 @@
|
||||
#include <SDL.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include "app/gfx/backend/irenderer.h"
|
||||
#include "util/sdl_deleter.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gfx {
|
||||
|
||||
void Arena::Initialize(IRenderer* renderer) { renderer_ = renderer; }
|
||||
|
||||
Arena& Arena::Get() {
|
||||
static Arena instance;
|
||||
return instance;
|
||||
@@ -23,74 +26,95 @@ Arena::~Arena() {
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allocate a new SDL texture with automatic cleanup and resource pooling
|
||||
* @param renderer SDL renderer for texture creation
|
||||
* @param width Texture width in pixels
|
||||
* @param height Texture height in pixels
|
||||
* @return Pointer to allocated texture (managed by Arena)
|
||||
*
|
||||
* Performance Notes:
|
||||
* - Uses RGBA8888 format for maximum compatibility
|
||||
* - STREAMING access for dynamic updates (common in ROM editing)
|
||||
* - Resource pooling for 30% memory reduction
|
||||
* - Automatic cleanup via unique_ptr with custom deleter
|
||||
* - Hash map storage for O(1) lookup and management
|
||||
*/
|
||||
SDL_Texture* Arena::AllocateTexture(SDL_Renderer* renderer, int width,
|
||||
int height) {
|
||||
if (!renderer) {
|
||||
SDL_Log("Invalid renderer passed to AllocateTexture");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (width <= 0 || height <= 0) {
|
||||
SDL_Log("Invalid texture dimensions: width=%d, height=%d", width, height);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Try to reuse existing texture of same size from pool
|
||||
for (auto it = texture_pool_.available_textures_.begin();
|
||||
it != texture_pool_.available_textures_.end(); ++it) {
|
||||
auto& size = texture_pool_.texture_sizes_[*it];
|
||||
if (size.first == width && size.second == height) {
|
||||
SDL_Texture* texture = *it;
|
||||
texture_pool_.available_textures_.erase(it);
|
||||
|
||||
// Store in hash map with automatic cleanup
|
||||
textures_[texture] =
|
||||
std::unique_ptr<SDL_Texture, util::SDL_Texture_Deleter>(texture);
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
|
||||
// Create new texture if none available in pool
|
||||
return CreateNewTexture(renderer, width, height);
|
||||
void Arena::QueueTextureCommand(TextureCommandType type, Bitmap* bitmap) {
|
||||
texture_command_queue_.push_back({type, bitmap});
|
||||
}
|
||||
|
||||
void Arena::FreeTexture(SDL_Texture* texture) {
|
||||
if (!texture) return;
|
||||
void Arena::ProcessTextureQueue(IRenderer* renderer) {
|
||||
if (!renderer_) return;
|
||||
|
||||
auto it = textures_.find(texture);
|
||||
if (it != textures_.end()) {
|
||||
// Return to pool instead of destroying if pool has space
|
||||
if (texture_pool_.available_textures_.size() < texture_pool_.MAX_POOL_SIZE) {
|
||||
// Get texture dimensions before releasing
|
||||
int width, height;
|
||||
SDL_QueryTexture(texture, nullptr, nullptr, &width, &height);
|
||||
texture_pool_.texture_sizes_[texture] = {width, height};
|
||||
texture_pool_.available_textures_.push_back(texture);
|
||||
|
||||
// Release from unique_ptr without destroying
|
||||
it->second.release();
|
||||
for (const auto& command : texture_command_queue_) {
|
||||
switch (command.type) {
|
||||
case TextureCommandType::CREATE: {
|
||||
// Create a new texture and update it with bitmap data
|
||||
if (command.bitmap && command.bitmap->surface() &&
|
||||
command.bitmap->surface()->format &&
|
||||
command.bitmap->is_active() &&
|
||||
command.bitmap->width() > 0 && command.bitmap->height() > 0) {
|
||||
auto texture = renderer_->CreateTexture(command.bitmap->width(),
|
||||
command.bitmap->height());
|
||||
if (texture) {
|
||||
command.bitmap->set_texture(texture);
|
||||
renderer_->UpdateTexture(texture, *command.bitmap);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TextureCommandType::UPDATE: {
|
||||
// Update existing texture with current bitmap data
|
||||
if (command.bitmap && command.bitmap->texture() &&
|
||||
command.bitmap->surface() && command.bitmap->surface()->format &&
|
||||
command.bitmap->is_active()) {
|
||||
renderer_->UpdateTexture(command.bitmap->texture(), *command.bitmap);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TextureCommandType::DESTROY: {
|
||||
if (command.bitmap && command.bitmap->texture()) {
|
||||
renderer_->DestroyTexture(command.bitmap->texture());
|
||||
command.bitmap->set_texture(nullptr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
textures_.erase(it);
|
||||
}
|
||||
texture_command_queue_.clear();
|
||||
}
|
||||
|
||||
SDL_Surface* Arena::AllocateSurface(int width, int height, int depth, int format) {
|
||||
// Try to get a surface from the pool first
|
||||
for (auto it = surface_pool_.available_surfaces_.begin();
|
||||
it != surface_pool_.available_surfaces_.end(); ++it) {
|
||||
auto& info = surface_pool_.surface_info_[*it];
|
||||
if (std::get<0>(info) == width && std::get<1>(info) == height &&
|
||||
std::get<2>(info) == depth && std::get<3>(info) == format) {
|
||||
SDL_Surface* surface = *it;
|
||||
surface_pool_.available_surfaces_.erase(it);
|
||||
return surface;
|
||||
}
|
||||
}
|
||||
|
||||
// Create new surface if none available in pool
|
||||
Uint32 sdl_format = GetSnesPixelFormat(format);
|
||||
SDL_Surface* surface = SDL_CreateRGBSurfaceWithFormat(0, width, height, depth, sdl_format);
|
||||
|
||||
if (surface) {
|
||||
auto surface_ptr = std::unique_ptr<SDL_Surface, util::SDL_Surface_Deleter>(surface);
|
||||
surfaces_[surface] = std::move(surface_ptr);
|
||||
surface_pool_.surface_info_[surface] = std::make_tuple(width, height, depth, format);
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
void Arena::FreeSurface(SDL_Surface* surface) {
|
||||
if (!surface) return;
|
||||
|
||||
// Return surface to pool if space available
|
||||
if (surface_pool_.available_surfaces_.size() < surface_pool_.MAX_POOL_SIZE) {
|
||||
surface_pool_.available_surfaces_.push_back(surface);
|
||||
} else {
|
||||
// Remove from tracking maps
|
||||
surface_pool_.surface_info_.erase(surface);
|
||||
surfaces_.erase(surface);
|
||||
}
|
||||
}
|
||||
|
||||
void Arena::Shutdown() {
|
||||
// Process any remaining batch updates before shutdown
|
||||
ProcessBatchTextureUpdates();
|
||||
ProcessTextureQueue(renderer_);
|
||||
|
||||
// Clear pool references first to prevent reuse during shutdown
|
||||
surface_pool_.available_surfaces_.clear();
|
||||
@@ -104,406 +128,9 @@ void Arena::Shutdown() {
|
||||
surfaces_.clear();
|
||||
|
||||
// Clear any remaining queue items
|
||||
batch_update_queue_.clear();
|
||||
texture_command_queue_.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update texture data from surface (with format conversion)
|
||||
* @param texture Target texture to update
|
||||
* @param surface Source surface with pixel data
|
||||
*
|
||||
* Performance Notes:
|
||||
* - Converts surface to RGBA8888 format for texture compatibility
|
||||
* - Uses memcpy for efficient pixel data transfer
|
||||
* - Handles format conversion automatically
|
||||
* - Locks texture for direct pixel access
|
||||
*
|
||||
* ROM Hacking Specific:
|
||||
* - Supports indexed color surfaces (common in SNES graphics)
|
||||
* - Handles palette-based graphics conversion
|
||||
* - Optimized for frequent updates during editing
|
||||
*/
|
||||
void Arena::UpdateTexture(SDL_Texture* texture, SDL_Surface* surface) {
|
||||
if (!texture || !surface) {
|
||||
SDL_Log("Invalid texture or surface passed to UpdateTexture");
|
||||
return;
|
||||
}
|
||||
|
||||
if (surface->pixels == nullptr) {
|
||||
SDL_Log("Surface pixels are nullptr");
|
||||
return;
|
||||
}
|
||||
|
||||
// Additional safety checks to prevent crashes
|
||||
if (surface->w <= 0 || surface->h <= 0) {
|
||||
SDL_Log("Invalid surface dimensions: %dx%d", surface->w, surface->h);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!surface->format) {
|
||||
SDL_Log("Surface format is nullptr");
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert surface to RGBA8888 format for texture compatibility
|
||||
auto converted_surface =
|
||||
std::unique_ptr<SDL_Surface, util::SDL_Surface_Deleter>(
|
||||
SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_RGBA8888, 0),
|
||||
util::SDL_Surface_Deleter());
|
||||
|
||||
if (!converted_surface) {
|
||||
SDL_Log("SDL_ConvertSurfaceFormat failed: %s", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
// Additional validation for converted surface
|
||||
if (!converted_surface->pixels) {
|
||||
SDL_Log("Converted surface pixels are nullptr");
|
||||
return;
|
||||
}
|
||||
|
||||
if (converted_surface->w <= 0 || converted_surface->h <= 0) {
|
||||
SDL_Log("Invalid converted surface dimensions: %dx%d", converted_surface->w, converted_surface->h);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate texture before locking
|
||||
int texture_w, texture_h;
|
||||
if (SDL_QueryTexture(texture, nullptr, nullptr, &texture_w, &texture_h) != 0) {
|
||||
SDL_Log("SDL_QueryTexture failed: %s", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
if (texture_w != converted_surface->w || texture_h != converted_surface->h) {
|
||||
SDL_Log("Texture/surface size mismatch: texture=%dx%d, surface=%dx%d",
|
||||
texture_w, texture_h, converted_surface->w, converted_surface->h);
|
||||
return;
|
||||
}
|
||||
|
||||
// Lock texture for direct pixel access
|
||||
void* pixels;
|
||||
int pitch;
|
||||
if (SDL_LockTexture(texture, nullptr, &pixels, &pitch) != 0) {
|
||||
SDL_Log("SDL_LockTexture failed: %s", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
// Additional safety check for locked pixels
|
||||
if (!pixels) {
|
||||
SDL_Log("Locked texture pixels are nullptr");
|
||||
SDL_UnlockTexture(texture);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate copy size to prevent buffer overrun
|
||||
size_t copy_size = converted_surface->h * converted_surface->pitch;
|
||||
size_t max_texture_size = texture_h * pitch;
|
||||
|
||||
if (copy_size > max_texture_size) {
|
||||
SDL_Log("Copy size (%zu) exceeds texture capacity (%zu)", copy_size, max_texture_size);
|
||||
SDL_UnlockTexture(texture);
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy pixel data efficiently with bounds checking
|
||||
memcpy(pixels, converted_surface->pixels, copy_size);
|
||||
|
||||
SDL_UnlockTexture(texture);
|
||||
}
|
||||
|
||||
SDL_Surface* Arena::AllocateSurface(int width, int height, int depth,
|
||||
int format) {
|
||||
// Try to reuse existing surface of same size and format from pool
|
||||
for (auto it = surface_pool_.available_surfaces_.begin();
|
||||
it != surface_pool_.available_surfaces_.end(); ++it) {
|
||||
auto& info = surface_pool_.surface_info_[*it];
|
||||
if (std::get<0>(info) == width && std::get<1>(info) == height &&
|
||||
std::get<2>(info) == depth && std::get<3>(info) == format) {
|
||||
SDL_Surface* surface = *it;
|
||||
surface_pool_.available_surfaces_.erase(it);
|
||||
|
||||
// Clear the surface pixels before reusing for safety
|
||||
if (surface && surface->pixels) {
|
||||
memset(surface->pixels, 0, surface->h * surface->pitch);
|
||||
}
|
||||
|
||||
// Store in hash map with automatic cleanup
|
||||
surfaces_[surface] =
|
||||
std::unique_ptr<SDL_Surface, util::SDL_Surface_Deleter>(surface);
|
||||
return surface;
|
||||
}
|
||||
}
|
||||
|
||||
// Create new surface if none available in pool
|
||||
return CreateNewSurface(width, height, depth, format);
|
||||
}
|
||||
|
||||
|
||||
void Arena::FreeSurface(SDL_Surface* surface) {
|
||||
if (!surface) return;
|
||||
|
||||
auto it = surfaces_.find(surface);
|
||||
if (it != surfaces_.end()) {
|
||||
// Return to pool instead of destroying if pool has space
|
||||
if (surface_pool_.available_surfaces_.size() < surface_pool_.MAX_POOL_SIZE) {
|
||||
// Get surface info before releasing
|
||||
int width = surface->w;
|
||||
int height = surface->h;
|
||||
int depth = surface->format->BitsPerPixel;
|
||||
int format = surface->format->format;
|
||||
surface_pool_.surface_info_[surface] = {width, height, depth, format};
|
||||
surface_pool_.available_surfaces_.push_back(surface);
|
||||
|
||||
// Release from unique_ptr without destroying
|
||||
it->second.release();
|
||||
}
|
||||
surfaces_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new SDL texture (helper for resource pooling)
|
||||
* @param renderer SDL renderer for texture creation
|
||||
* @param width Texture width in pixels
|
||||
* @param height Texture height in pixels
|
||||
* @return Pointer to allocated texture (managed by Arena)
|
||||
*/
|
||||
SDL_Texture* Arena::CreateNewTexture(SDL_Renderer* renderer, int width, int height) {
|
||||
SDL_Texture* texture =
|
||||
SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,
|
||||
SDL_TEXTUREACCESS_STREAMING, width, height);
|
||||
if (!texture) {
|
||||
SDL_Log("Failed to create texture: %s", SDL_GetError());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Store in hash map with automatic cleanup
|
||||
textures_[texture] =
|
||||
std::unique_ptr<SDL_Texture, util::SDL_Texture_Deleter>(texture);
|
||||
return texture;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new SDL surface (helper for resource pooling)
|
||||
* @param width Surface width in pixels
|
||||
* @param height Surface height in pixels
|
||||
* @param depth Color depth in bits per pixel
|
||||
* @param format SDL pixel format
|
||||
* @return Pointer to allocated surface (managed by Arena)
|
||||
*/
|
||||
SDL_Surface* Arena::CreateNewSurface(int width, int height, int depth, int format) {
|
||||
SDL_Surface* surface =
|
||||
SDL_CreateRGBSurfaceWithFormat(0, width, height, depth, format);
|
||||
if (!surface) {
|
||||
SDL_Log("Failed to create surface: %s", SDL_GetError());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Store in hash map with automatic cleanup
|
||||
surfaces_[surface] =
|
||||
std::unique_ptr<SDL_Surface, util::SDL_Surface_Deleter>(surface);
|
||||
return surface;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update texture data from surface for a specific region
|
||||
* @param texture Target texture to update
|
||||
* @param surface Source surface with pixel data
|
||||
* @param rect Region to update (nullptr for entire texture)
|
||||
*
|
||||
* Performance Notes:
|
||||
* - Region-specific updates for efficiency
|
||||
* - Converts surface to RGBA8888 format for texture compatibility
|
||||
* - Uses memcpy for efficient pixel data transfer
|
||||
* - Handles format conversion automatically
|
||||
*/
|
||||
void Arena::UpdateTextureRegion(SDL_Texture* texture, SDL_Surface* surface, SDL_Rect* rect) {
|
||||
if (!texture || !surface) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (surface->pixels == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert surface to RGBA8888 format for texture compatibility
|
||||
auto converted_surface =
|
||||
std::unique_ptr<SDL_Surface, util::SDL_Surface_Deleter>(
|
||||
SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_RGBA8888, 0),
|
||||
util::SDL_Surface_Deleter());
|
||||
|
||||
if (!converted_surface) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Lock texture for direct pixel access
|
||||
void* pixels;
|
||||
int pitch;
|
||||
if (SDL_LockTexture(texture, rect, &pixels, &pitch) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy pixel data efficiently with bounds checking
|
||||
if (rect) {
|
||||
// Validate rect bounds against surface dimensions
|
||||
int max_x = std::min(rect->x + rect->w, converted_surface->w);
|
||||
int max_y = std::min(rect->y + rect->h, converted_surface->h);
|
||||
int safe_x = std::max(0, rect->x);
|
||||
int safe_y = std::max(0, rect->y);
|
||||
int safe_w = max_x - safe_x;
|
||||
int safe_h = max_y - safe_y;
|
||||
|
||||
|
||||
if (safe_w > 0 && safe_h > 0) {
|
||||
// Copy only the safe region
|
||||
int src_offset = safe_y * converted_surface->pitch + safe_x * 4; // 4 bytes per RGBA pixel
|
||||
int dst_offset = 0;
|
||||
for (int y = 0; y < safe_h; y++) {
|
||||
// Additional safety check for each row
|
||||
if (src_offset + safe_w * 4 <= converted_surface->h * converted_surface->pitch) {
|
||||
memcpy(static_cast<char*>(pixels) + dst_offset,
|
||||
static_cast<char*>(converted_surface->pixels) + src_offset,
|
||||
safe_w * 4);
|
||||
}
|
||||
src_offset += converted_surface->pitch;
|
||||
dst_offset += pitch;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Copy entire surface
|
||||
memcpy(pixels, converted_surface->pixels,
|
||||
converted_surface->h * converted_surface->pitch);
|
||||
}
|
||||
|
||||
SDL_UnlockTexture(texture);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Queue a texture update for batch processing
|
||||
* @param texture Target texture to update
|
||||
* @param surface Source surface with pixel data
|
||||
* @param rect Region to update (nullptr for entire texture)
|
||||
*
|
||||
* Performance Notes:
|
||||
* - Queues updates instead of processing immediately
|
||||
* - Reduces SDL calls by batching multiple updates
|
||||
* - Automatic queue size management to prevent memory bloat
|
||||
*/
|
||||
void Arena::QueueTextureUpdate(SDL_Texture* texture, SDL_Surface* surface, SDL_Rect* rect) {
|
||||
if (!texture || !surface) {
|
||||
SDL_Log("Invalid texture or surface passed to QueueTextureUpdate");
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent queue from growing too large
|
||||
if (batch_update_queue_.size() >= MAX_BATCH_SIZE) {
|
||||
ProcessBatchTextureUpdates();
|
||||
}
|
||||
|
||||
batch_update_queue_.emplace_back(texture, surface, rect);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process all queued texture updates in a single batch
|
||||
* @note This reduces SDL calls and improves performance significantly
|
||||
*
|
||||
* Performance Notes:
|
||||
* - Processes all queued updates in one operation
|
||||
* - Reduces SDL context switching overhead
|
||||
* - Optimized for multiple small updates
|
||||
* - Clears queue after processing
|
||||
*/
|
||||
void Arena::ProcessBatchTextureUpdates() {
|
||||
if (batch_update_queue_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Process all queued updates with minimal logging
|
||||
for (const auto& update : batch_update_queue_) {
|
||||
// Validate pointers before processing
|
||||
if (!update.texture || !update.surface) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (update.rect) {
|
||||
UpdateTextureRegion(update.texture, update.surface, update.rect.get());
|
||||
} else {
|
||||
UpdateTexture(update.texture, update.surface);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the queue after processing
|
||||
batch_update_queue_.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear all queued texture updates
|
||||
* @note Useful for cleanup or when batch processing is not needed
|
||||
*/
|
||||
void Arena::ClearBatchQueue() {
|
||||
batch_update_queue_.clear();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Progressive/Deferred Texture Management
|
||||
// ============================================================================
|
||||
|
||||
void Arena::QueueDeferredTexture(gfx::Bitmap* bitmap, int priority) {
|
||||
if (!bitmap) return;
|
||||
|
||||
std::lock_guard<std::mutex> lock(deferred_mutex_);
|
||||
deferred_textures_.emplace_back(bitmap, priority);
|
||||
}
|
||||
|
||||
std::vector<gfx::Bitmap*> Arena::GetNextDeferredTextureBatch(
|
||||
int high_priority_limit, int low_priority_limit) {
|
||||
std::lock_guard<std::mutex> lock(deferred_mutex_);
|
||||
|
||||
std::vector<gfx::Bitmap*> batch;
|
||||
|
||||
if (deferred_textures_.empty()) {
|
||||
return batch;
|
||||
}
|
||||
|
||||
// Sort by priority (lower number = higher priority)
|
||||
std::sort(deferred_textures_.begin(), deferred_textures_.end(),
|
||||
[](const DeferredTexture& a, const DeferredTexture& b) {
|
||||
return a.priority < b.priority;
|
||||
});
|
||||
|
||||
// Phase 1: Collect high-priority items (priority 0-10)
|
||||
auto it = deferred_textures_.begin();
|
||||
while (it != deferred_textures_.end() && batch.size() < static_cast<size_t>(high_priority_limit)) {
|
||||
if (it->bitmap && it->priority <= 10 && !it->bitmap->texture()) {
|
||||
batch.push_back(it->bitmap);
|
||||
it = deferred_textures_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 2: Collect low-priority items (priority 11+) if we have capacity
|
||||
if (batch.size() < static_cast<size_t>(high_priority_limit)) {
|
||||
it = deferred_textures_.begin();
|
||||
int low_count = 0;
|
||||
while (it != deferred_textures_.end() && low_count < low_priority_limit) {
|
||||
if (it->bitmap && it->priority > 10 && !it->bitmap->texture()) {
|
||||
batch.push_back(it->bitmap);
|
||||
low_count++;
|
||||
it = deferred_textures_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return batch;
|
||||
}
|
||||
|
||||
void Arena::ClearDeferredTextures() {
|
||||
std::lock_guard<std::mutex> lock(deferred_mutex_);
|
||||
deferred_textures_.clear();
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace yaze
|
||||
@@ -45,76 +45,23 @@ class Arena {
|
||||
public:
|
||||
static Arena& Get();
|
||||
|
||||
void Initialize(IRenderer* renderer);
|
||||
~Arena();
|
||||
|
||||
// Resource management
|
||||
/**
|
||||
* @brief Allocate a new SDL texture with automatic cleanup
|
||||
* @param renderer SDL renderer for texture creation
|
||||
* @param width Texture width in pixels
|
||||
* @param height Texture height in pixels
|
||||
* @return Pointer to allocated texture (managed by Arena)
|
||||
*/
|
||||
SDL_Texture* AllocateTexture(SDL_Renderer* renderer, int width, int height);
|
||||
|
||||
/**
|
||||
* @brief Free a texture and remove from Arena management
|
||||
* @param texture Texture to free
|
||||
*/
|
||||
void FreeTexture(SDL_Texture* texture);
|
||||
|
||||
/**
|
||||
* @brief Update texture data from surface (with format conversion)
|
||||
* @param texture Target texture to update
|
||||
* @param surface Source surface with pixel data
|
||||
*/
|
||||
void UpdateTexture(SDL_Texture* texture, SDL_Surface* surface);
|
||||
// --- New Deferred Command System ---
|
||||
enum class TextureCommandType { CREATE, UPDATE, DESTROY };
|
||||
struct TextureCommand {
|
||||
TextureCommandType type;
|
||||
Bitmap* bitmap; // The bitmap that needs a texture operation
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Update texture data from surface for a specific region
|
||||
* @param texture Target texture to update
|
||||
* @param surface Source surface with pixel data
|
||||
* @param rect Region to update (nullptr for entire texture)
|
||||
*/
|
||||
void UpdateTextureRegion(SDL_Texture* texture, SDL_Surface* surface, SDL_Rect* rect = nullptr);
|
||||
void QueueTextureCommand(TextureCommandType type, Bitmap* bitmap);
|
||||
void ProcessTextureQueue(IRenderer* renderer);
|
||||
|
||||
// Batch operations for improved performance
|
||||
/**
|
||||
* @brief Queue a texture update for batch processing
|
||||
* @param texture Target texture to update
|
||||
* @param surface Source surface with pixel data
|
||||
* @param rect Region to update (nullptr for entire texture)
|
||||
*/
|
||||
void QueueTextureUpdate(SDL_Texture* texture, SDL_Surface* surface, SDL_Rect* rect = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Process all queued texture updates in a single batch
|
||||
* @note This reduces SDL calls and improves performance significantly
|
||||
*/
|
||||
void ProcessBatchTextureUpdates();
|
||||
|
||||
/**
|
||||
* @brief Clear all queued texture updates
|
||||
*/
|
||||
void ClearBatchQueue();
|
||||
|
||||
/**
|
||||
* @brief Allocate a new SDL surface with automatic cleanup
|
||||
* @param width Surface width in pixels
|
||||
* @param height Surface height in pixels
|
||||
* @param depth Color depth in bits per pixel
|
||||
* @param format SDL pixel format
|
||||
* @return Pointer to allocated surface (managed by Arena)
|
||||
*/
|
||||
// --- Surface Management (unchanged) ---
|
||||
SDL_Surface* AllocateSurface(int width, int height, int depth, int format);
|
||||
|
||||
/**
|
||||
* @brief Free a surface and remove from Arena management
|
||||
* @param surface Surface to free
|
||||
*/
|
||||
void FreeSurface(SDL_Surface* surface);
|
||||
|
||||
// Explicit cleanup method for controlled shutdown
|
||||
void Shutdown();
|
||||
|
||||
// Resource tracking for debugging
|
||||
@@ -163,41 +110,6 @@ class Arena {
|
||||
*/
|
||||
auto& bg2() { return bg2_; }
|
||||
|
||||
// Progressive/Deferred Texture Management (for large asset loading)
|
||||
/**
|
||||
* @brief Add a bitmap to the deferred texture queue
|
||||
* @param bitmap Bitmap that needs a texture created
|
||||
* @param priority Higher priority items processed first (0 = highest)
|
||||
*
|
||||
* Use this for progressive loading of large asset sets (e.g., overworld maps).
|
||||
* Textures are created incrementally per frame to avoid UI freezes.
|
||||
*/
|
||||
void QueueDeferredTexture(gfx::Bitmap* bitmap, int priority = 0);
|
||||
|
||||
/**
|
||||
* @brief Get next batch of deferred textures to process
|
||||
* @param high_priority_limit Max high-priority items to return
|
||||
* @param low_priority_limit Max low-priority items to return
|
||||
* @return Vector of bitmaps to render (caller renders them via Renderer)
|
||||
*
|
||||
* Call this once per frame in your editor's Update() method, then render each bitmap.
|
||||
* High-priority items (priority 0-10) returned up to high_priority_limit.
|
||||
* Low-priority items (priority 11+) returned up to low_priority_limit.
|
||||
*/
|
||||
std::vector<gfx::Bitmap*> GetNextDeferredTextureBatch(int high_priority_limit = 4,
|
||||
int low_priority_limit = 2);
|
||||
|
||||
/**
|
||||
* @brief Clear all deferred texture items
|
||||
*/
|
||||
void ClearDeferredTextures();
|
||||
|
||||
/**
|
||||
* @brief Get count of remaining deferred textures
|
||||
* @return Number of bitmaps waiting for textures
|
||||
*/
|
||||
size_t GetDeferredTextureCount() const { return deferred_textures_.size(); }
|
||||
|
||||
private:
|
||||
Arena();
|
||||
|
||||
@@ -213,7 +125,7 @@ class Arena {
|
||||
|
||||
std::array<gfx::Bitmap, 223> gfx_sheets_;
|
||||
|
||||
std::unordered_map<SDL_Texture*,
|
||||
std::unordered_map<TextureHandle,
|
||||
std::unique_ptr<SDL_Texture, util::SDL_Texture_Deleter>>
|
||||
textures_;
|
||||
|
||||
@@ -223,8 +135,8 @@ class Arena {
|
||||
|
||||
// Resource pooling for efficient memory management
|
||||
struct TexturePool {
|
||||
std::vector<SDL_Texture*> available_textures_;
|
||||
std::unordered_map<SDL_Texture*, std::pair<int, int>> texture_sizes_;
|
||||
std::vector<TextureHandle> available_textures_;
|
||||
std::unordered_map<TextureHandle, std::pair<int, int>> texture_sizes_;
|
||||
static constexpr size_t MAX_POOL_SIZE = 100;
|
||||
} texture_pool_;
|
||||
|
||||
@@ -234,32 +146,8 @@ class Arena {
|
||||
static constexpr size_t MAX_POOL_SIZE = 100;
|
||||
} surface_pool_;
|
||||
|
||||
// Batch operations for improved performance
|
||||
struct BatchUpdate {
|
||||
SDL_Texture* texture;
|
||||
SDL_Surface* surface;
|
||||
std::unique_ptr<SDL_Rect> rect;
|
||||
|
||||
BatchUpdate(SDL_Texture* t, SDL_Surface* s, SDL_Rect* r = nullptr)
|
||||
: texture(t), surface(s), rect(r ? std::make_unique<SDL_Rect>(*r) : nullptr) {}
|
||||
};
|
||||
|
||||
std::vector<BatchUpdate> batch_update_queue_;
|
||||
static constexpr size_t MAX_BATCH_SIZE = 50;
|
||||
|
||||
// Helper methods for resource pooling
|
||||
SDL_Texture* CreateNewTexture(SDL_Renderer* renderer, int width, int height);
|
||||
SDL_Surface* CreateNewSurface(int width, int height, int depth, int format);
|
||||
|
||||
// Progressive loading infrastructure
|
||||
struct DeferredTexture {
|
||||
gfx::Bitmap* bitmap;
|
||||
int priority;
|
||||
|
||||
DeferredTexture(gfx::Bitmap* bmp, int prio) : bitmap(bmp), priority(prio) {}
|
||||
};
|
||||
std::vector<DeferredTexture> deferred_textures_;
|
||||
std::mutex deferred_mutex_;
|
||||
std::vector<TextureCommand> texture_command_queue_;
|
||||
IRenderer* renderer_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
|
||||
@@ -12,7 +12,7 @@ AtlasRenderer& AtlasRenderer::Get() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
void AtlasRenderer::Initialize(SDL_Renderer* renderer, int initial_size) {
|
||||
void AtlasRenderer::Initialize(IRenderer* renderer, int initial_size) {
|
||||
renderer_ = renderer;
|
||||
next_atlas_id_ = 0;
|
||||
current_atlas_ = 0;
|
||||
@@ -37,15 +37,10 @@ int AtlasRenderer::AddBitmap(const Bitmap& bitmap) {
|
||||
int atlas_id = next_atlas_id_++;
|
||||
auto& atlas = *atlases_[current_atlas_];
|
||||
|
||||
// Create atlas entry with BPP format information
|
||||
BppFormat bpp_format = BppFormatManager::Get().DetectFormat(bitmap.vector(), bitmap.width(), bitmap.height());
|
||||
atlas.entries.emplace_back(atlas_id, uv_rect, bitmap.texture(), bpp_format, bitmap.width(), bitmap.height());
|
||||
atlas_lookup_[atlas_id] = &atlas.entries.back();
|
||||
|
||||
// Copy bitmap data to atlas texture
|
||||
SDL_SetRenderTarget(renderer_, atlas.texture);
|
||||
SDL_RenderCopy(renderer_, bitmap.texture(), nullptr, &uv_rect);
|
||||
SDL_SetRenderTarget(renderer_, nullptr);
|
||||
renderer_->SetRenderTarget(atlas.texture);
|
||||
renderer_->RenderCopy(bitmap.texture(), nullptr, &uv_rect);
|
||||
renderer_->SetRenderTarget(nullptr);
|
||||
|
||||
return atlas_id;
|
||||
}
|
||||
@@ -61,9 +56,9 @@ int AtlasRenderer::AddBitmap(const Bitmap& bitmap) {
|
||||
atlas_lookup_[atlas_id] = &atlas.entries.back();
|
||||
|
||||
// Copy bitmap data to atlas texture
|
||||
SDL_SetRenderTarget(renderer_, atlas.texture);
|
||||
SDL_RenderCopy(renderer_, bitmap.texture(), nullptr, &uv_rect);
|
||||
SDL_SetRenderTarget(renderer_, nullptr);
|
||||
renderer_->SetRenderTarget(atlas.texture);
|
||||
renderer_->RenderCopy(bitmap.texture(), nullptr, &uv_rect);
|
||||
renderer_->SetRenderTarget(nullptr);
|
||||
|
||||
return atlas_id;
|
||||
}
|
||||
@@ -92,7 +87,7 @@ int AtlasRenderer::AddBitmapWithBppOptimization(const Bitmap& bitmap, BppFormat
|
||||
|
||||
// Create temporary bitmap with converted data
|
||||
Bitmap converted_bitmap(bitmap.width(), bitmap.height(), bitmap.depth(), converted_data, bitmap.palette());
|
||||
converted_bitmap.CreateTexture(renderer_);
|
||||
converted_bitmap.CreateTexture();
|
||||
|
||||
// Add converted bitmap to atlas
|
||||
return AddBitmap(converted_bitmap);
|
||||
@@ -169,7 +164,7 @@ void AtlasRenderer::RenderBatch(const std::vector<RenderCommand>& render_command
|
||||
auto& atlas = *atlases_[atlas_index];
|
||||
|
||||
// Set atlas texture
|
||||
SDL_SetTextureBlendMode(atlas.texture, SDL_BLENDMODE_BLEND);
|
||||
// SDL_SetTextureBlendMode(atlas.texture, SDL_BLENDMODE_BLEND);
|
||||
|
||||
// Render all commands for this atlas
|
||||
for (const auto* cmd : commands) {
|
||||
@@ -190,9 +185,9 @@ void AtlasRenderer::RenderBatch(const std::vector<RenderCommand>& render_command
|
||||
if (std::abs(cmd->rotation) > 0.001F) {
|
||||
// For rotation, we'd need to use SDL_RenderCopyEx
|
||||
// This is a simplified version
|
||||
SDL_RenderCopy(renderer_, atlas.texture, &entry->uv_rect, &dest_rect);
|
||||
renderer_->RenderCopy(atlas.texture, &entry->uv_rect, &dest_rect);
|
||||
} else {
|
||||
SDL_RenderCopy(renderer_, atlas.texture, &entry->uv_rect, &dest_rect);
|
||||
renderer_->RenderCopy(atlas.texture, &entry->uv_rect, &dest_rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,7 +233,7 @@ void AtlasRenderer::RenderBatchWithBppOptimization(const std::vector<RenderComma
|
||||
auto& atlas = *atlases_[atlas_index];
|
||||
|
||||
// Set atlas texture with BPP-specific blend mode
|
||||
SDL_SetTextureBlendMode(atlas.texture, SDL_BLENDMODE_BLEND);
|
||||
// SDL_SetTextureBlendMode(atlas.texture, SDL_BLENDMODE_BLEND);
|
||||
|
||||
// Render all commands for this atlas and BPP format
|
||||
for (const auto* cmd : commands) {
|
||||
@@ -257,9 +252,9 @@ void AtlasRenderer::RenderBatchWithBppOptimization(const std::vector<RenderComma
|
||||
|
||||
// Apply rotation if needed
|
||||
if (std::abs(cmd->rotation) > 0.001F) {
|
||||
SDL_RenderCopy(renderer_, atlas.texture, &entry->uv_rect, &dest_rect);
|
||||
renderer_->RenderCopy(atlas.texture, &entry->uv_rect, &dest_rect);
|
||||
} else {
|
||||
SDL_RenderCopy(renderer_, atlas.texture, &entry->uv_rect, &dest_rect);
|
||||
renderer_->RenderCopy(atlas.texture, &entry->uv_rect, &dest_rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,7 +301,7 @@ void AtlasRenderer::Clear() {
|
||||
// Clean up SDL textures
|
||||
for (auto& atlas : atlases_) {
|
||||
if (atlas->texture) {
|
||||
SDL_DestroyTexture(atlas->texture);
|
||||
renderer_->DestroyTexture(atlas->texture);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,8 +336,8 @@ void AtlasRenderer::RenderBitmap(int atlas_id, float x, float y, float scale_x,
|
||||
};
|
||||
|
||||
// Render using atlas texture
|
||||
SDL_SetTextureBlendMode(atlas->texture, SDL_BLENDMODE_BLEND);
|
||||
SDL_RenderCopy(renderer_, atlas->texture, &entry->uv_rect, &dest_rect);
|
||||
// SDL_SetTextureBlendMode(atlas->texture, SDL_BLENDMODE_BLEND);
|
||||
renderer_->RenderCopy(atlas->texture, &entry->uv_rect, &dest_rect);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -393,8 +388,7 @@ void AtlasRenderer::CreateNewAtlas() {
|
||||
|
||||
// Create SDL texture for the atlas
|
||||
auto& atlas = *atlases_[current_atlas_];
|
||||
atlas.texture = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888,
|
||||
SDL_TEXTUREACCESS_TARGET, size, size);
|
||||
atlas.texture = renderer_->CreateTexture(size, size);
|
||||
|
||||
if (!atlas.texture) {
|
||||
SDL_Log("Failed to create atlas texture: %s", SDL_GetError());
|
||||
@@ -406,18 +400,18 @@ void AtlasRenderer::RebuildAtlas(Atlas& atlas) {
|
||||
std::fill(atlas.used_regions.begin(), atlas.used_regions.end(), false);
|
||||
|
||||
// Rebuild atlas texture by copying from source textures
|
||||
SDL_SetRenderTarget(renderer_, atlas.texture);
|
||||
SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0);
|
||||
SDL_RenderClear(renderer_);
|
||||
renderer_->SetRenderTarget(atlas.texture);
|
||||
renderer_->SetDrawColor({0, 0, 0, 0});
|
||||
renderer_->Clear();
|
||||
|
||||
for (auto& entry : atlas.entries) {
|
||||
if (entry.in_use && entry.texture) {
|
||||
SDL_RenderCopy(renderer_, entry.texture, nullptr, &entry.uv_rect);
|
||||
renderer_->RenderCopy(entry.texture, nullptr, &entry.uv_rect);
|
||||
MarkRegionUsed(atlas, entry.uv_rect, true);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_SetRenderTarget(renderer_, nullptr);
|
||||
renderer_->SetRenderTarget(nullptr);
|
||||
}
|
||||
|
||||
SDL_Rect AtlasRenderer::FindFreeRegion(Atlas& atlas, int width, int height) {
|
||||
|
||||
@@ -77,10 +77,10 @@ class AtlasRenderer {
|
||||
|
||||
/**
|
||||
* @brief Initialize the atlas renderer
|
||||
* @param renderer SDL renderer for texture operations
|
||||
* @param renderer The renderer to use for texture operations
|
||||
* @param initial_size Initial atlas size (power of 2 recommended)
|
||||
*/
|
||||
void Initialize(SDL_Renderer* renderer, int initial_size = 1024);
|
||||
void Initialize(IRenderer* renderer, int initial_size = 1024);
|
||||
|
||||
/**
|
||||
* @brief Add a bitmap to the atlas
|
||||
@@ -164,20 +164,20 @@ class AtlasRenderer {
|
||||
struct AtlasEntry {
|
||||
int atlas_id;
|
||||
SDL_Rect uv_rect; // UV coordinates in atlas
|
||||
SDL_Texture* texture;
|
||||
TextureHandle texture;
|
||||
bool in_use;
|
||||
BppFormat bpp_format; // BPP format of this entry
|
||||
int original_width;
|
||||
int original_height;
|
||||
|
||||
AtlasEntry(int id, const SDL_Rect& rect, SDL_Texture* tex, BppFormat bpp = BppFormat::kBpp8,
|
||||
AtlasEntry(int id, const SDL_Rect& rect, TextureHandle tex, BppFormat bpp = BppFormat::kBpp8,
|
||||
int width = 0, int height = 0)
|
||||
: atlas_id(id), uv_rect(rect), texture(tex), in_use(true),
|
||||
bpp_format(bpp), original_width(width), original_height(height) {}
|
||||
};
|
||||
|
||||
struct Atlas {
|
||||
SDL_Texture* texture;
|
||||
TextureHandle texture;
|
||||
int size;
|
||||
std::vector<AtlasEntry> entries;
|
||||
std::vector<bool> used_regions; // Track used regions for packing
|
||||
@@ -185,7 +185,7 @@ class AtlasRenderer {
|
||||
Atlas(int s) : size(s), used_regions(s * s, false) {}
|
||||
};
|
||||
|
||||
SDL_Renderer* renderer_;
|
||||
IRenderer* renderer_;
|
||||
std::vector<std::unique_ptr<Atlas>> atlases_;
|
||||
std::unordered_map<int, AtlasEntry*> atlas_lookup_;
|
||||
int next_atlas_id_;
|
||||
|
||||
@@ -56,13 +56,24 @@ TextureHandle SDL2Renderer::CreateTexture(int width, int height) {
|
||||
*/
|
||||
void SDL2Renderer::UpdateTexture(TextureHandle texture, const Bitmap& bitmap) {
|
||||
SDL_Surface* surface = bitmap.surface();
|
||||
if (!texture || !surface) return;
|
||||
|
||||
// Validate texture, surface, and surface format
|
||||
if (!texture || !surface || !surface->format) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate surface has pixels
|
||||
if (!surface->pixels || surface->w <= 0 || surface->h <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert the bitmap's surface to RGBA8888 format for compatibility with the texture.
|
||||
auto converted_surface = std::unique_ptr<SDL_Surface, util::SDL_Surface_Deleter>(
|
||||
SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_RGBA8888, 0));
|
||||
|
||||
if (!converted_surface) return;
|
||||
if (!converted_surface || !converted_surface->pixels) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the texture with the pixels from the converted surface.
|
||||
SDL_UpdateTexture(static_cast<SDL_Texture*>(texture), nullptr, converted_surface->pixels, converted_surface->pitch);
|
||||
|
||||
@@ -218,6 +218,11 @@ void Bitmap::Create(int width, int height, int depth, int format,
|
||||
SDL_UnlockSurface(surface_);
|
||||
}
|
||||
active_ = true;
|
||||
|
||||
// Apply the stored palette if one exists
|
||||
if (!palette_.empty()) {
|
||||
ApplyStoredPalette();
|
||||
}
|
||||
}
|
||||
|
||||
void Bitmap::Reformat(int format) {
|
||||
@@ -234,124 +239,38 @@ void Bitmap::Reformat(int format) {
|
||||
SetPalette(palette_);
|
||||
}
|
||||
|
||||
void Bitmap::UpdateTexture(SDL_Renderer *renderer) {
|
||||
if (!texture_) {
|
||||
CreateTexture(renderer);
|
||||
return;
|
||||
}
|
||||
|
||||
// Use direct SDL calls for reliable texture updates
|
||||
if (modified_ && surface_ && surface_->pixels) {
|
||||
// Convert surface to RGBA8888 format for texture compatibility
|
||||
SDL_Surface* converted = SDL_ConvertSurfaceFormat(surface_, SDL_PIXELFORMAT_RGBA8888, 0);
|
||||
if (converted) {
|
||||
// Update texture directly with SDL
|
||||
int result = SDL_UpdateTexture(texture_, nullptr, converted->pixels, converted->pitch);
|
||||
if (result != 0) {
|
||||
SDL_Log("SDL_UpdateTexture failed: %s", SDL_GetError());
|
||||
}
|
||||
SDL_FreeSurface(converted);
|
||||
} else {
|
||||
SDL_Log("SDL_ConvertSurfaceFormat failed: %s", SDL_GetError());
|
||||
}
|
||||
modified_ = false;
|
||||
}
|
||||
void Bitmap::CreateTexture() {
|
||||
Arena::Get().QueueTextureCommand(Arena::TextureCommandType::CREATE, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Queue texture update for batch processing (improved performance)
|
||||
* @param renderer SDL renderer for texture operations
|
||||
* @note Use this for better performance when multiple textures need updating
|
||||
*
|
||||
* Performance Notes:
|
||||
* - Queues updates instead of processing immediately
|
||||
* - Reduces SDL calls by batching multiple updates
|
||||
* - 5x faster for multiple texture updates
|
||||
* - Automatic dirty region handling
|
||||
*/
|
||||
void Bitmap::QueueTextureUpdate(SDL_Renderer *renderer) {
|
||||
ScopedTimer timer("texture_batch_queue");
|
||||
|
||||
if (!texture_) {
|
||||
CreateTexture(renderer);
|
||||
return;
|
||||
}
|
||||
|
||||
// Only queue if there are dirty regions
|
||||
if (!dirty_region_.is_dirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Queue the dirty region update for batch processing
|
||||
if (dirty_region_.is_dirty) {
|
||||
// Ensure dirty rect is within bounds
|
||||
int rect_x = std::max(0, dirty_region_.min_x);
|
||||
int rect_y = std::max(0, dirty_region_.min_y);
|
||||
int rect_w = std::min(width_ - rect_x, dirty_region_.max_x - dirty_region_.min_x + 1);
|
||||
int rect_h = std::min(height_ - rect_y, dirty_region_.max_y - dirty_region_.min_y + 1);
|
||||
|
||||
// Only proceed if we have a valid rect
|
||||
if (rect_w > 0 && rect_h > 0) {
|
||||
SDL_Rect dirty_rect = { rect_x, rect_y, rect_w, rect_h };
|
||||
Arena::Get().QueueTextureUpdate(texture_, surface_, &dirty_rect);
|
||||
}
|
||||
dirty_region_.Reset();
|
||||
}
|
||||
void Bitmap::UpdateTexture() {
|
||||
Arena::Get().QueueTextureCommand(Arena::TextureCommandType::UPDATE, this);
|
||||
}
|
||||
|
||||
void Bitmap::CreateTexture(SDL_Renderer *renderer) {
|
||||
if (!renderer) {
|
||||
SDL_Log("Invalid renderer passed to CreateTexture");
|
||||
return;
|
||||
}
|
||||
|
||||
if (width_ <= 0 || height_ <= 0) {
|
||||
SDL_Log("Invalid texture dimensions: width=%d, height=%d\n", width_,
|
||||
height_);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get a texture from the Arena
|
||||
texture_ = Arena::Get().AllocateTexture(renderer, width_, height_);
|
||||
if (!texture_) {
|
||||
SDL_Log("Bitmap::CreateTexture failed to allocate texture: %s\n",
|
||||
SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateTextureData();
|
||||
}
|
||||
|
||||
void Bitmap::UpdateTextureData() {
|
||||
if (!texture_ || !surface_) {
|
||||
return;
|
||||
}
|
||||
|
||||
Arena::Get().UpdateTexture(texture_, surface_);
|
||||
modified_ = false;
|
||||
}
|
||||
|
||||
void Bitmap::SetPalette(const SnesPalette &palette) {
|
||||
void Bitmap::ApplyStoredPalette() {
|
||||
if (surface_ == nullptr) {
|
||||
throw BitmapError("Surface is null. Palette not applied");
|
||||
return; // Can't apply without surface
|
||||
}
|
||||
if (surface_->format == nullptr || surface_->format->palette == nullptr) {
|
||||
throw BitmapError(
|
||||
"Surface format or palette is null. Palette not applied.");
|
||||
return; // Can't apply palette to this surface format
|
||||
}
|
||||
if (palette_.empty()) {
|
||||
return; // No palette to apply
|
||||
}
|
||||
palette_ = palette;
|
||||
|
||||
// Invalidate palette cache when palette changes
|
||||
InvalidatePaletteCache();
|
||||
|
||||
SDL_Palette *sdl_palette = surface_->format->palette;
|
||||
if (sdl_palette == nullptr) {
|
||||
throw BitmapError("Failed to get SDL palette");
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_UnlockSurface(surface_);
|
||||
for (size_t i = 0; i < palette.size(); ++i) {
|
||||
const auto& pal_color = palette[i];
|
||||
for (size_t i = 0; i < palette_.size() && i < 256; ++i) {
|
||||
const auto& pal_color = palette_[i];
|
||||
sdl_palette->colors[i].r = pal_color.rgb().x;
|
||||
sdl_palette->colors[i].g = pal_color.rgb().y;
|
||||
sdl_palette->colors[i].b = pal_color.rgb().z;
|
||||
@@ -360,10 +279,22 @@ void Bitmap::SetPalette(const SnesPalette &palette) {
|
||||
SDL_LockSurface(surface_);
|
||||
}
|
||||
|
||||
void Bitmap::SetPalette(const SnesPalette &palette) {
|
||||
// Store palette even if surface isn't ready yet
|
||||
palette_ = palette;
|
||||
|
||||
// Apply it immediately if surface is ready
|
||||
ApplyStoredPalette();
|
||||
}
|
||||
|
||||
void Bitmap::SetPaletteWithTransparent(const SnesPalette &palette, size_t index,
|
||||
int length) {
|
||||
// Store palette even if surface isn't ready yet
|
||||
palette_ = palette;
|
||||
|
||||
// If surface isn't created yet, just store the palette for later
|
||||
if (surface_ == nullptr) {
|
||||
throw BitmapError("Surface is null. Palette not applied");
|
||||
return; // Palette will be applied when surface is created
|
||||
}
|
||||
|
||||
// CRITICAL FIX: Use index directly as palette slot, not index * 7
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "app/gfx/backend/irenderer.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
|
||||
namespace yaze {
|
||||
@@ -136,19 +137,19 @@ class Bitmap {
|
||||
/**
|
||||
* @brief Creates the underlying SDL_Texture to be displayed.
|
||||
*/
|
||||
void CreateTexture(SDL_Renderer *renderer);
|
||||
void CreateTexture();
|
||||
|
||||
/**
|
||||
* @brief Updates the underlying SDL_Texture when it already exists.
|
||||
*/
|
||||
void UpdateTexture(SDL_Renderer *renderer);
|
||||
void UpdateTexture();
|
||||
|
||||
/**
|
||||
* @brief Queue texture update for batch processing (improved performance)
|
||||
* @param renderer SDL renderer for texture operations
|
||||
* @note Use this for better performance when multiple textures need updating
|
||||
*/
|
||||
void QueueTextureUpdate(SDL_Renderer *renderer);
|
||||
void QueueTextureUpdate(IRenderer *renderer);
|
||||
|
||||
/**
|
||||
* @brief Updates the texture data from the surface
|
||||
@@ -166,6 +167,11 @@ class Bitmap {
|
||||
void SetPaletteWithTransparent(const SnesPalette &palette, size_t index,
|
||||
int length = 7);
|
||||
|
||||
/**
|
||||
* @brief Apply the stored palette to the surface (internal helper)
|
||||
*/
|
||||
void ApplyStoredPalette();
|
||||
|
||||
/**
|
||||
* @brief Set the palette using SDL colors
|
||||
*/
|
||||
@@ -251,7 +257,7 @@ class Bitmap {
|
||||
const uint8_t *data() const { return data_.data(); }
|
||||
std::vector<uint8_t> &mutable_data() { return data_; }
|
||||
SDL_Surface *surface() const { return surface_; }
|
||||
SDL_Texture *texture() const { return texture_; }
|
||||
TextureHandle texture() const { return texture_; }
|
||||
const std::vector<uint8_t> &vector() const { return data_; }
|
||||
uint8_t at(int i) const { return data_[i]; }
|
||||
bool modified() const { return modified_; }
|
||||
@@ -259,6 +265,7 @@ class Bitmap {
|
||||
void set_active(bool active) { active_ = active; }
|
||||
void set_data(const std::vector<uint8_t> &data);
|
||||
void set_modified(bool modified) { modified_ = modified; }
|
||||
void set_texture(TextureHandle texture) { texture_ = texture; }
|
||||
|
||||
|
||||
private:
|
||||
@@ -285,7 +292,7 @@ class Bitmap {
|
||||
SDL_Surface *surface_ = nullptr;
|
||||
|
||||
// Texture for the bitmap (managed by Arena)
|
||||
SDL_Texture *texture_ = nullptr;
|
||||
TextureHandle texture_ = nullptr;
|
||||
|
||||
// Optimized palette lookup cache for O(1) color index lookups
|
||||
std::unordered_map<uint32_t, uint8_t> color_to_index_cache_;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "app/core/window.h"
|
||||
#include "app/gfx/arena.h"
|
||||
#include "app/gfx/atlas_renderer.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
@@ -12,7 +11,7 @@
|
||||
namespace yaze {
|
||||
namespace gfx {
|
||||
|
||||
Tilemap CreateTilemap(std::vector<uint8_t> &data, int width, int height,
|
||||
Tilemap CreateTilemap(IRenderer* renderer, std::vector<uint8_t> &data, int width, int height,
|
||||
int tile_size, int num_tiles, SnesPalette &palette) {
|
||||
Tilemap tilemap;
|
||||
tilemap.tile_size.x = tile_size;
|
||||
@@ -21,16 +20,28 @@ Tilemap CreateTilemap(std::vector<uint8_t> &data, int width, int height,
|
||||
tilemap.map_size.y = num_tiles;
|
||||
tilemap.atlas = Bitmap(width, height, 8, data);
|
||||
tilemap.atlas.SetPalette(palette);
|
||||
core::Renderer::Get().RenderBitmap(&tilemap.atlas);
|
||||
|
||||
// Queue texture creation directly via Arena
|
||||
if (tilemap.atlas.is_active() && tilemap.atlas.surface()) {
|
||||
Arena::Get().QueueTextureCommand(Arena::TextureCommandType::CREATE, &tilemap.atlas);
|
||||
}
|
||||
|
||||
return tilemap;
|
||||
}
|
||||
|
||||
void UpdateTilemap(Tilemap &tilemap, const std::vector<uint8_t> &data) {
|
||||
void UpdateTilemap(IRenderer* renderer, Tilemap &tilemap, const std::vector<uint8_t> &data) {
|
||||
tilemap.atlas.set_data(data);
|
||||
core::Renderer::Get().UpdateBitmap(&tilemap.atlas);
|
||||
|
||||
// Queue texture update directly via Arena
|
||||
if (tilemap.atlas.texture() && tilemap.atlas.is_active() && tilemap.atlas.surface()) {
|
||||
Arena::Get().QueueTextureCommand(Arena::TextureCommandType::UPDATE, &tilemap.atlas);
|
||||
} else if (!tilemap.atlas.texture() && tilemap.atlas.is_active() && tilemap.atlas.surface()) {
|
||||
// Create if doesn't exist yet
|
||||
Arena::Get().QueueTextureCommand(Arena::TextureCommandType::CREATE, &tilemap.atlas);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderTile(Tilemap &tilemap, int tile_id) {
|
||||
void RenderTile(IRenderer* renderer, Tilemap &tilemap, int tile_id) {
|
||||
// Validate tilemap state before proceeding
|
||||
if (!tilemap.atlas.is_active() || tilemap.atlas.vector().empty()) {
|
||||
return;
|
||||
@@ -49,7 +60,7 @@ void RenderTile(Tilemap &tilemap, int tile_id) {
|
||||
// Note: Tile cache disabled to prevent std::move() related crashes
|
||||
}
|
||||
|
||||
void RenderTile16(Tilemap &tilemap, int tile_id) {
|
||||
void RenderTile16(IRenderer* renderer, Tilemap &tilemap, int tile_id) {
|
||||
// Validate tilemap state before proceeding
|
||||
if (!tilemap.atlas.is_active() || tilemap.atlas.vector().empty()) {
|
||||
return;
|
||||
@@ -76,7 +87,7 @@ void RenderTile16(Tilemap &tilemap, int tile_id) {
|
||||
// Note: Tile cache disabled to prevent std::move() related crashes
|
||||
}
|
||||
|
||||
void UpdateTile16(Tilemap &tilemap, int tile_id) {
|
||||
void UpdateTile16(IRenderer* renderer, Tilemap &tilemap, int tile_id) {
|
||||
// Check if tile is cached
|
||||
Bitmap* cached_tile = tilemap.tile_cache.GetTile(tile_id);
|
||||
if (cached_tile) {
|
||||
@@ -88,10 +99,14 @@ void UpdateTile16(Tilemap &tilemap, int tile_id) {
|
||||
int tile_data_offset = 0;
|
||||
tilemap.atlas.Get16x16Tile(tile_x, tile_y, tile_data, tile_data_offset);
|
||||
cached_tile->set_data(tile_data);
|
||||
core::Renderer::Get().UpdateBitmap(cached_tile);
|
||||
|
||||
// Queue texture update directly via Arena
|
||||
if (cached_tile->texture() && cached_tile->is_active()) {
|
||||
Arena::Get().QueueTextureCommand(Arena::TextureCommandType::UPDATE, cached_tile);
|
||||
}
|
||||
} else {
|
||||
// Tile not cached, render it fresh
|
||||
RenderTile16(tilemap, tile_id);
|
||||
RenderTile16(renderer, tilemap, tile_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,7 +308,7 @@ std::vector<uint8_t> GetTilemapData(Tilemap &tilemap, int tile_id) {
|
||||
return data;
|
||||
}
|
||||
|
||||
void RenderTilesBatch(Tilemap& tilemap, const std::vector<int>& tile_ids,
|
||||
void RenderTilesBatch(IRenderer* renderer, Tilemap& tilemap, const std::vector<int>& tile_ids,
|
||||
const std::vector<std::pair<float, float>>& positions,
|
||||
const std::vector<std::pair<float, float>>& scales) {
|
||||
if (tile_ids.empty() || positions.empty() || tile_ids.size() != positions.size()) {
|
||||
@@ -302,9 +317,6 @@ void RenderTilesBatch(Tilemap& tilemap, const std::vector<int>& tile_ids,
|
||||
|
||||
ScopedTimer timer("tilemap_batch_render");
|
||||
|
||||
// Get renderer from Arena
|
||||
SDL_Renderer* renderer = nullptr; // We need to get this from the renderer system
|
||||
|
||||
// Initialize atlas renderer if not already done
|
||||
auto& atlas_renderer = AtlasRenderer::Get();
|
||||
if (!renderer) {
|
||||
@@ -340,11 +352,16 @@ void RenderTilesBatch(Tilemap& tilemap, const std::vector<int>& tile_ids,
|
||||
tilemap.tile_cache.CacheTile(tile_id, std::move(new_tile));
|
||||
cached_tile = tilemap.tile_cache.GetTile(tile_id);
|
||||
if (cached_tile) {
|
||||
core::Renderer::Get().RenderBitmap(cached_tile);
|
||||
cached_tile->CreateTexture();
|
||||
}
|
||||
}
|
||||
|
||||
if (cached_tile && cached_tile->is_active()) {
|
||||
// Queue texture creation if needed
|
||||
if (!cached_tile->texture() && cached_tile->surface()) {
|
||||
Arena::Get().QueueTextureCommand(Arena::TextureCommandType::CREATE, cached_tile);
|
||||
}
|
||||
|
||||
// Add to atlas renderer
|
||||
int atlas_id = atlas_renderer.AddBitmap(*cached_tile);
|
||||
if (atlas_id >= 0) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define YAZE_GFX_TILEMAP_H
|
||||
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "app/gfx/backend/irenderer.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
|
||||
@@ -116,15 +117,15 @@ struct Tilemap {
|
||||
std::vector<uint8_t> FetchTileDataFromGraphicsBuffer(
|
||||
const std::vector<uint8_t> &data, int tile_id, int sheet_offset);
|
||||
|
||||
Tilemap CreateTilemap(std::vector<uint8_t> &data, int width, int height,
|
||||
Tilemap CreateTilemap(IRenderer* renderer, std::vector<uint8_t> &data, int width, int height,
|
||||
int tile_size, int num_tiles, SnesPalette &palette);
|
||||
|
||||
void UpdateTilemap(Tilemap &tilemap, const std::vector<uint8_t> &data);
|
||||
void UpdateTilemap(IRenderer* renderer, Tilemap &tilemap, const std::vector<uint8_t> &data);
|
||||
|
||||
void RenderTile(Tilemap &tilemap, int tile_id);
|
||||
void RenderTile(IRenderer* renderer, Tilemap &tilemap, int tile_id);
|
||||
|
||||
void RenderTile16(Tilemap &tilemap, int tile_id);
|
||||
void UpdateTile16(Tilemap &tilemap, int tile_id);
|
||||
void RenderTile16(IRenderer* renderer, Tilemap &tilemap, int tile_id);
|
||||
void UpdateTile16(IRenderer* renderer, Tilemap &tilemap, int tile_id);
|
||||
|
||||
void ModifyTile16(Tilemap &tilemap, const std::vector<uint8_t> &data,
|
||||
const TileInfo &top_left, const TileInfo &top_right,
|
||||
@@ -146,7 +147,7 @@ std::vector<uint8_t> GetTilemapData(Tilemap &tilemap, int tile_id);
|
||||
* @param scales Vector of scale factors for each tile (optional, defaults to 1.0)
|
||||
* @note This function uses atlas rendering to reduce draw calls significantly
|
||||
*/
|
||||
void RenderTilesBatch(Tilemap& tilemap, const std::vector<int>& tile_ids,
|
||||
void RenderTilesBatch(IRenderer* renderer, Tilemap& tilemap, const std::vector<int>& tile_ids,
|
||||
const std::vector<std::pair<float, float>>& positions,
|
||||
const std::vector<std::pair<float, float>>& scales = {});
|
||||
|
||||
|
||||
@@ -3,40 +3,36 @@
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include "app/gfx/bpp_format_manager.h"
|
||||
|
||||
#include "app/core/window.h"
|
||||
#include "app/gfx/atlas_renderer.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/performance_profiler.h"
|
||||
#include "app/gui/canvas_utils.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "app/gui/style.h"
|
||||
#include "app/gui/canvas/canvas_automation_api.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
#include "util/log.h"
|
||||
|
||||
namespace yaze::gui {
|
||||
|
||||
using core::Renderer;
|
||||
|
||||
// Define constructors and destructor in .cc to avoid incomplete type issues with unique_ptr
|
||||
Canvas::Canvas() { InitializeDefaults(); }
|
||||
|
||||
// Default constructor
|
||||
Canvas::Canvas() : renderer_(nullptr) { InitializeDefaults(); }
|
||||
|
||||
// Legacy constructors (renderer is optional for backward compatibility)
|
||||
Canvas::Canvas(const std::string& id)
|
||||
: canvas_id_(id), context_id_(id + "Context") {
|
||||
: renderer_(nullptr), canvas_id_(id), context_id_(id + "Context") {
|
||||
InitializeDefaults();
|
||||
}
|
||||
|
||||
Canvas::Canvas(const std::string& id, ImVec2 canvas_size)
|
||||
: canvas_id_(id), context_id_(id + "Context") {
|
||||
: renderer_(nullptr), canvas_id_(id), context_id_(id + "Context") {
|
||||
InitializeDefaults();
|
||||
config_.canvas_size = canvas_size;
|
||||
config_.custom_canvas_size = true;
|
||||
}
|
||||
|
||||
Canvas::Canvas(const std::string& id, ImVec2 canvas_size, CanvasGridSize grid_size)
|
||||
: canvas_id_(id), context_id_(id + "Context") {
|
||||
: renderer_(nullptr), canvas_id_(id), context_id_(id + "Context") {
|
||||
InitializeDefaults();
|
||||
config_.canvas_size = canvas_size;
|
||||
config_.custom_canvas_size = true;
|
||||
@@ -44,7 +40,39 @@ Canvas::Canvas(const std::string& id, ImVec2 canvas_size, CanvasGridSize grid_si
|
||||
}
|
||||
|
||||
Canvas::Canvas(const std::string& id, ImVec2 canvas_size, CanvasGridSize grid_size, float global_scale)
|
||||
: canvas_id_(id), context_id_(id + "Context") {
|
||||
: renderer_(nullptr), canvas_id_(id), context_id_(id + "Context") {
|
||||
InitializeDefaults();
|
||||
config_.canvas_size = canvas_size;
|
||||
config_.custom_canvas_size = true;
|
||||
config_.global_scale = global_scale;
|
||||
SetGridSize(grid_size);
|
||||
}
|
||||
|
||||
// New constructors with renderer support (for migration to IRenderer pattern)
|
||||
Canvas::Canvas(gfx::IRenderer* renderer) : renderer_(renderer) { InitializeDefaults(); }
|
||||
|
||||
Canvas::Canvas(gfx::IRenderer* renderer, const std::string& id)
|
||||
: renderer_(renderer), canvas_id_(id), context_id_(id + "Context") {
|
||||
InitializeDefaults();
|
||||
}
|
||||
|
||||
Canvas::Canvas(gfx::IRenderer* renderer, const std::string& id, ImVec2 canvas_size)
|
||||
: renderer_(renderer), canvas_id_(id), context_id_(id + "Context") {
|
||||
InitializeDefaults();
|
||||
config_.canvas_size = canvas_size;
|
||||
config_.custom_canvas_size = true;
|
||||
}
|
||||
|
||||
Canvas::Canvas(gfx::IRenderer* renderer, const std::string& id, ImVec2 canvas_size, CanvasGridSize grid_size)
|
||||
: renderer_(renderer), canvas_id_(id), context_id_(id + "Context") {
|
||||
InitializeDefaults();
|
||||
config_.canvas_size = canvas_size;
|
||||
config_.custom_canvas_size = true;
|
||||
SetGridSize(grid_size);
|
||||
}
|
||||
|
||||
Canvas::Canvas(gfx::IRenderer* renderer, const std::string& id, ImVec2 canvas_size, CanvasGridSize grid_size, float global_scale)
|
||||
: renderer_(renderer), canvas_id_(id), context_id_(id + "Context") {
|
||||
InitializeDefaults();
|
||||
config_.canvas_size = canvas_size;
|
||||
config_.custom_canvas_size = true;
|
||||
@@ -243,8 +271,7 @@ void Canvas::ShowColorAnalysis() {
|
||||
|
||||
bool Canvas::ApplyROMPalette(int group_index, int palette_index) {
|
||||
if (palette_editor_ && bitmap_) {
|
||||
return palette_editor_->ApplyROMPalette(bitmap_, group_index,
|
||||
palette_index);
|
||||
return palette_editor_->ApplyROMPalette(bitmap_, group_index, palette_index);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -330,7 +357,7 @@ void Canvas::End() {
|
||||
|
||||
// ==================== Legacy Interface ====================
|
||||
|
||||
void Canvas::UpdateColorPainter(gfx::Bitmap& bitmap, const ImVec4& color,
|
||||
void Canvas::UpdateColorPainter(gfx::IRenderer* renderer, gfx::Bitmap& bitmap, const ImVec4& color,
|
||||
const std::function<void()>& event,
|
||||
int tile_size, float scale) {
|
||||
config_.global_scale = scale;
|
||||
@@ -340,6 +367,7 @@ void Canvas::UpdateColorPainter(gfx::Bitmap& bitmap, const ImVec4& color,
|
||||
DrawBitmap(bitmap, 2, scale);
|
||||
if (DrawSolidTilePainter(color, tile_size)) {
|
||||
event();
|
||||
bitmap.UpdateTexture();
|
||||
}
|
||||
DrawGrid();
|
||||
DrawOverlay();
|
||||
@@ -1796,7 +1824,7 @@ bool Canvas::ConvertBitmapFormat(gfx::BppFormat target_format) {
|
||||
bitmap_->set_data(converted_data);
|
||||
|
||||
// Update the renderer
|
||||
core::Renderer::Get().UpdateBitmap(bitmap_);
|
||||
bitmap_->UpdateTexture();
|
||||
|
||||
return true;
|
||||
} catch (const std::exception& e) {
|
||||
|
||||
@@ -53,13 +53,26 @@ enum class CanvasGridSize { k8x8, k16x16, k32x32, k64x64 };
|
||||
*/
|
||||
class Canvas {
|
||||
public:
|
||||
// Default constructor
|
||||
Canvas();
|
||||
~Canvas();
|
||||
|
||||
// Legacy constructors (renderer is optional for backward compatibility)
|
||||
explicit Canvas(const std::string& id);
|
||||
explicit Canvas(const std::string& id, ImVec2 canvas_size);
|
||||
explicit Canvas(const std::string& id, ImVec2 canvas_size, CanvasGridSize grid_size);
|
||||
explicit Canvas(const std::string& id, ImVec2 canvas_size, CanvasGridSize grid_size, float global_scale);
|
||||
|
||||
// New constructors with renderer support (for migration to IRenderer pattern)
|
||||
explicit Canvas(gfx::IRenderer* renderer);
|
||||
explicit Canvas(gfx::IRenderer* renderer, const std::string& id);
|
||||
explicit Canvas(gfx::IRenderer* renderer, const std::string& id, ImVec2 canvas_size);
|
||||
explicit Canvas(gfx::IRenderer* renderer, const std::string& id, ImVec2 canvas_size, CanvasGridSize grid_size);
|
||||
explicit Canvas(gfx::IRenderer* renderer, const std::string& id, ImVec2 canvas_size, CanvasGridSize grid_size, float global_scale);
|
||||
|
||||
// Set renderer after construction (for late initialization)
|
||||
void SetRenderer(gfx::IRenderer* renderer) { renderer_ = renderer; }
|
||||
gfx::IRenderer* renderer() const { return renderer_; }
|
||||
|
||||
void SetGridSize(CanvasGridSize grid_size) {
|
||||
switch (grid_size) {
|
||||
@@ -89,7 +102,7 @@ class Canvas {
|
||||
return CanvasGridSize::k16x16; // Default
|
||||
}
|
||||
|
||||
void UpdateColorPainter(gfx::Bitmap &bitmap, const ImVec4 &color,
|
||||
void UpdateColorPainter(gfx::IRenderer* renderer, gfx::Bitmap &bitmap, const ImVec4 &color,
|
||||
const std::function<void()> &event, int tile_size,
|
||||
float scale = 1.0f);
|
||||
|
||||
@@ -248,20 +261,6 @@ class Canvas {
|
||||
bool WasDoubleClicked(ImGuiMouseButton button = ImGuiMouseButton_Left) const;
|
||||
ImVec2 GetLastClickPosition() const;
|
||||
|
||||
private:
|
||||
void DrawContextMenuItem(const ContextMenuItem& item);
|
||||
|
||||
// Tile painter shows a preview of the currently selected tile
|
||||
// and allows the user to left click to paint the tile or right
|
||||
// click to select a new tile to paint with.
|
||||
// (Moved to public section)
|
||||
|
||||
// Draws a tile on the canvas at the specified position
|
||||
// (Moved to public section)
|
||||
|
||||
// These methods are now public - see public section above
|
||||
|
||||
public:
|
||||
// Tile painter methods
|
||||
bool DrawTilePainter(const Bitmap &bitmap, int size, float scale = 1.0f);
|
||||
bool DrawSolidTilePainter(const ImVec4 &color, int size);
|
||||
@@ -401,7 +400,10 @@ class Canvas {
|
||||
Rom *rom() const { return rom_; }
|
||||
|
||||
private:
|
||||
void DrawContextMenuItem(const ContextMenuItem& item);
|
||||
|
||||
// Modular configuration and state
|
||||
gfx::IRenderer* renderer_ = nullptr;
|
||||
CanvasConfig config_;
|
||||
CanvasSelection selection_;
|
||||
std::unique_ptr<PaletteWidget> palette_editor_;
|
||||
@@ -506,13 +508,21 @@ void TableCanvasPipeline(gui::Canvas &canvas, gfx::Bitmap &bitmap,
|
||||
class ScopedCanvas {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct and begin a new canvas
|
||||
* @brief Construct and begin a new canvas (legacy constructor without renderer)
|
||||
*/
|
||||
explicit ScopedCanvas(const std::string& id, ImVec2 canvas_size = ImVec2(0, 0))
|
||||
: canvas_(new Canvas(id, canvas_size)), owned_(true), active_(true) {
|
||||
canvas_->Begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct and begin a new canvas (with optional renderer)
|
||||
*/
|
||||
explicit ScopedCanvas(gfx::IRenderer* renderer, const std::string& id, ImVec2 canvas_size = ImVec2(0, 0))
|
||||
: canvas_(new Canvas(renderer, id, canvas_size)), owned_(true), active_(true) {
|
||||
canvas_->Begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wrap existing canvas with RAII
|
||||
*/
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
#include "canvas_context_menu.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "app/core/window.h"
|
||||
#include "app/gfx/arena.h"
|
||||
#include "app/gfx/performance_profiler.h"
|
||||
#include "app/gfx/performance_dashboard.h"
|
||||
#include "app/gui/widgets/palette_widget.h"
|
||||
#include "app/gui/bpp_format_ui.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "app/gui/canvas/canvas_modals.h"
|
||||
@@ -303,15 +298,21 @@ void CanvasContextMenu::RenderBitmapOperationsMenu(gfx::Bitmap* bitmap) {
|
||||
if (ImGui::BeginMenu("Format")) {
|
||||
if (ImGui::MenuItem("Indexed")) {
|
||||
bitmap->Reformat(gfx::BitmapFormat::kIndexed);
|
||||
core::Renderer::Get().UpdateBitmap(bitmap);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, bitmap);
|
||||
}
|
||||
if (ImGui::MenuItem("4BPP")) {
|
||||
bitmap->Reformat(gfx::BitmapFormat::k4bpp);
|
||||
core::Renderer::Get().UpdateBitmap(bitmap);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, bitmap);
|
||||
}
|
||||
if (ImGui::MenuItem("8BPP")) {
|
||||
bitmap->Reformat(gfx::BitmapFormat::k8bpp);
|
||||
core::Renderer::Get().UpdateBitmap(bitmap);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, bitmap);
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "canvas_utils.h"
|
||||
|
||||
#include <cmath>
|
||||
#include "app/core/window.h"
|
||||
#include "app/gfx/arena.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "util/log.h"
|
||||
|
||||
@@ -9,8 +9,6 @@ namespace yaze {
|
||||
namespace gui {
|
||||
namespace CanvasUtils {
|
||||
|
||||
using core::Renderer;
|
||||
|
||||
ImVec2 AlignToGrid(ImVec2 pos, float grid_step) {
|
||||
return ImVec2(std::floor(pos.x / grid_step) * grid_step,
|
||||
std::floor(pos.y / grid_step) * grid_step);
|
||||
@@ -95,35 +93,30 @@ bool LoadROMPaletteGroups(Rom* rom, CanvasPaletteManager& palette_manager) {
|
||||
}
|
||||
}
|
||||
|
||||
bool ApplyPaletteGroup(gfx::Bitmap* bitmap,
|
||||
const CanvasPaletteManager& palette_manager,
|
||||
bool ApplyPaletteGroup(gfx::IRenderer* renderer, gfx::Bitmap* bitmap, const CanvasPaletteManager& palette_manager,
|
||||
int group_index, int palette_index) {
|
||||
if (!bitmap || group_index < 0 ||
|
||||
group_index >=
|
||||
static_cast<int>(palette_manager.rom_palette_groups.size())) {
|
||||
if (!bitmap) return false;
|
||||
|
||||
if (group_index < 0 || group_index >= palette_manager.rom_palette_groups.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const auto& selected_palette =
|
||||
palette_manager.rom_palette_groups[group_index];
|
||||
|
||||
// Apply the palette based on the index
|
||||
if (palette_index >= 0 && palette_index < 8) {
|
||||
bitmap->SetPaletteWithTransparent(selected_palette, palette_index);
|
||||
} else {
|
||||
bitmap->SetPalette(selected_palette);
|
||||
}
|
||||
|
||||
Renderer::Get().UpdateBitmap(bitmap);
|
||||
LOG_DEBUG("Canvas", "Applied palette group %d, index %d to bitmap",
|
||||
group_index, palette_index);
|
||||
return true;
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR("Canvas", "Failed to apply palette");
|
||||
return false;
|
||||
const auto& palette = palette_manager.rom_palette_groups[group_index];
|
||||
|
||||
// Apply the full palette or use SetPaletteWithTransparent if palette_index is specified
|
||||
if (palette_index == 0) {
|
||||
bitmap->SetPalette(palette);
|
||||
} else {
|
||||
bitmap->SetPaletteWithTransparent(palette, palette_index);
|
||||
}
|
||||
bitmap->set_modified(true);
|
||||
|
||||
// Queue texture update via Arena's deferred system
|
||||
if (renderer) {
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, bitmap);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Drawing utility functions
|
||||
|
||||
@@ -89,7 +89,7 @@ int GetTileIdFromPosition(ImVec2 mouse_pos, float tile_size, float scale, int ti
|
||||
|
||||
// Palette management utilities
|
||||
bool LoadROMPaletteGroups(Rom* rom, CanvasPaletteManager& palette_manager);
|
||||
bool ApplyPaletteGroup(gfx::Bitmap* bitmap, const CanvasPaletteManager& palette_manager,
|
||||
bool ApplyPaletteGroup(gfx::IRenderer* renderer, gfx::Bitmap* bitmap, const CanvasPaletteManager& palette_manager,
|
||||
int group_index, int palette_index);
|
||||
|
||||
// Drawing utility functions (moved from Canvas class)
|
||||
|
||||
@@ -28,6 +28,12 @@ static inline ImGuiInputTextFlags InputScalar_DefaultCharsFilter(
|
||||
? ImGuiInputTextFlags_CharsHexadecimal
|
||||
: ImGuiInputTextFlags_CharsDecimal;
|
||||
}
|
||||
|
||||
// Helper: returns true if label is "invisible" (starts with "##")
|
||||
static inline bool IsInvisibleLabel(const char* label) {
|
||||
return label && label[0] == '#' && label[1] == '#';
|
||||
}
|
||||
|
||||
bool InputScalarLeft(const char* label, ImGuiDataType data_type, void* p_data,
|
||||
const void* p_step, const void* p_step_fast,
|
||||
const char* format, float input_width,
|
||||
@@ -52,22 +58,24 @@ bool InputScalarLeft(const char* label, ImGuiDataType data_type, void* p_data,
|
||||
flags |= ImGuiInputTextFlags_AutoSelectAll;
|
||||
|
||||
bool value_changed = false;
|
||||
// if (p_step == NULL) {
|
||||
// ImGui::SetNextItemWidth(input_width);
|
||||
// if (InputText("", buf, IM_ARRAYSIZE(buf), flags))
|
||||
// value_changed = DataTypeApplyFromText(buf, data_type, p_data, format);
|
||||
// } else {
|
||||
const float button_size = GetFrameHeight();
|
||||
AlignTextToFramePadding();
|
||||
Text("%s", label);
|
||||
SameLine();
|
||||
|
||||
// Support invisible labels (##) by not rendering the label, but still using it for ID
|
||||
bool invisible_label = IsInvisibleLabel(label);
|
||||
|
||||
if (!invisible_label) {
|
||||
AlignTextToFramePadding();
|
||||
Text("%s", label);
|
||||
SameLine();
|
||||
}
|
||||
|
||||
BeginGroup(); // The only purpose of the group here is to allow the caller
|
||||
// to query item data e.g. IsItemActive()
|
||||
PushID(label);
|
||||
SetNextItemWidth(ImMax(
|
||||
1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2));
|
||||
|
||||
// Place the label on the left of the input field
|
||||
// Place the label on the left of the input field, unless invisible
|
||||
PushStyleVar(ImGuiStyleVar_ItemSpacing,
|
||||
ImVec2{style.ItemSpacing.x, style.ItemSpacing.y});
|
||||
PushStyleVar(ImGuiStyleVar_FramePadding,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "util/file_util.h"
|
||||
#include "app/gui/theme_manager.h"
|
||||
#include "app/editor/ui/background_renderer.h"
|
||||
#include "app/core/platform/font_loader.h"
|
||||
#include "app/platform/font_loader.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "imgui/imgui.h"
|
||||
@@ -1293,7 +1293,7 @@ void DrawFontManager() {
|
||||
static bool font_selected = false;
|
||||
|
||||
ImGui::Text("Loaded fonts");
|
||||
for (const auto &loaded_font : core::font_registry.fonts) {
|
||||
for (const auto &loaded_font : font_registry.fonts) {
|
||||
ImGui::Text("%s", loaded_font.font_path);
|
||||
}
|
||||
ImGui::Separator();
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#endif
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
|
||||
namespace gui {
|
||||
|
||||
AgentChatWidget::AgentChatWidget()
|
||||
@@ -334,5 +334,5 @@ void AgentChatWidget::ScrollToBottom() {
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
} // namespace app
|
||||
|
||||
} // namespace yaze
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "app/rom.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
|
||||
namespace gui {
|
||||
|
||||
/**
|
||||
@@ -77,7 +77,7 @@ class AgentChatWidget {
|
||||
};
|
||||
|
||||
} // namespace gui
|
||||
} // namespace app
|
||||
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_GUI_WIDGETS_AGENT_CHAT_WIDGET_H_
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
|
||||
namespace gui {
|
||||
|
||||
CollaborationPanel::CollaborationPanel()
|
||||
@@ -660,5 +660,5 @@ void CollaborationPanel::RenderApprovalProposal(
|
||||
}
|
||||
|
||||
} // namespace gui
|
||||
} // namespace app
|
||||
|
||||
} // namespace yaze
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#endif
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
|
||||
namespace gui {
|
||||
|
||||
/**
|
||||
@@ -180,7 +180,7 @@ class CollaborationPanel {
|
||||
};
|
||||
|
||||
} // namespace gui
|
||||
} // namespace app
|
||||
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_GUI_WIDGETS_COLLABORATION_PANEL_H_
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "app/gui/widgets/dungeon_object_emulator_preview.h"
|
||||
#include "app/gfx/backend/irenderer.h"
|
||||
|
||||
#include "app/zelda3/dungeon/room.h"
|
||||
#include "app/zelda3/dungeon/room_object.h"
|
||||
@@ -11,23 +12,22 @@ namespace gui {
|
||||
|
||||
DungeonObjectEmulatorPreview::DungeonObjectEmulatorPreview() {
|
||||
snes_instance_ = std::make_unique<emu::Snes>();
|
||||
object_texture_ = SDL_CreateTexture(core::Renderer::Get().renderer(),
|
||||
SDL_PIXELFORMAT_ARGB8888,
|
||||
SDL_TEXTUREACCESS_STREAMING, 256, 256);
|
||||
}
|
||||
|
||||
DungeonObjectEmulatorPreview::~DungeonObjectEmulatorPreview() {
|
||||
if (object_texture_) {
|
||||
SDL_DestroyTexture(object_texture_);
|
||||
}
|
||||
// if (object_texture_) {
|
||||
// renderer_->DestroyTexture(object_texture_);
|
||||
// }
|
||||
}
|
||||
|
||||
void DungeonObjectEmulatorPreview::Initialize(Rom* rom) {
|
||||
rom_ = rom;
|
||||
if (rom_ && rom_->is_loaded()) {
|
||||
auto rom_data = rom_->vector();
|
||||
snes_instance_->Init(rom_data);
|
||||
}
|
||||
void DungeonObjectEmulatorPreview::Initialize(gfx::IRenderer* renderer, Rom* rom) {
|
||||
renderer_ = renderer;
|
||||
rom_ = rom;
|
||||
snes_instance_ = std::make_unique<emu::Snes>();
|
||||
std::vector<uint8_t> rom_data = rom->vector();
|
||||
snes_instance_->Init(rom_data);
|
||||
|
||||
// object_texture_ = renderer_->CreateTexture(256, 256);
|
||||
}
|
||||
|
||||
void DungeonObjectEmulatorPreview::Render() {
|
||||
@@ -286,9 +286,9 @@ void DungeonObjectEmulatorPreview::TriggerEmulatedRender() {
|
||||
// 15. Get the rendered pixels from PPU
|
||||
void* pixels = nullptr;
|
||||
int pitch = 0;
|
||||
if (SDL_LockTexture(object_texture_, nullptr, &pixels, &pitch) == 0) {
|
||||
if (renderer_->LockTexture(object_texture_, nullptr, &pixels, &pitch)) {
|
||||
snes_instance_->SetPixels(static_cast<uint8_t*>(pixels));
|
||||
SDL_UnlockTexture(object_texture_);
|
||||
renderer_->UnlockTexture(object_texture_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
#include "app/emu/snes.h"
|
||||
#include "app/rom.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gfx {
|
||||
class IRenderer;
|
||||
} // namespace gfx
|
||||
}
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
@@ -12,16 +18,17 @@ class DungeonObjectEmulatorPreview {
|
||||
DungeonObjectEmulatorPreview();
|
||||
~DungeonObjectEmulatorPreview();
|
||||
|
||||
void Initialize(Rom* rom);
|
||||
void Initialize(gfx::IRenderer* renderer, Rom* rom);
|
||||
void Render();
|
||||
|
||||
private:
|
||||
void RenderControls();
|
||||
void TriggerEmulatedRender();
|
||||
|
||||
gfx::IRenderer* renderer_ = nullptr;
|
||||
Rom* rom_ = nullptr;
|
||||
std::unique_ptr<emu::Snes> snes_instance_;
|
||||
SDL_Texture* object_texture_ = nullptr;
|
||||
void* object_texture_ = nullptr;
|
||||
|
||||
int object_id_ = 0;
|
||||
int room_id_ = 0;
|
||||
|
||||
@@ -2,15 +2,13 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include "app/core/window.h"
|
||||
#include "app/gfx/arena.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "util/log.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace gui {
|
||||
|
||||
using core::Renderer;
|
||||
|
||||
void PaletteWidget::Initialize(Rom* rom) {
|
||||
rom_ = rom;
|
||||
rom_palettes_loaded_ = false;
|
||||
@@ -169,7 +167,9 @@ bool PaletteWidget::ApplyROMPalette(gfx::Bitmap* bitmap, int group_index, int pa
|
||||
bitmap->SetPalette(selected_palette);
|
||||
}
|
||||
|
||||
Renderer::Get().UpdateBitmap(bitmap);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, bitmap);
|
||||
|
||||
current_group_index_ = group_index;
|
||||
current_palette_index_ = palette_index;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#if __APPLE__
|
||||
#include "app/core/platform/app_delegate.h"
|
||||
#include "app/platform/app_delegate.h"
|
||||
#endif
|
||||
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "absl/strings/str_format.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
|
||||
namespace net {
|
||||
|
||||
CollaborationService::CollaborationService(Rom* rom)
|
||||
@@ -436,5 +436,5 @@ bool CollaborationService::ShouldAutoSync() {
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace app
|
||||
|
||||
} // namespace yaze
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "app/rom.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
|
||||
namespace net {
|
||||
|
||||
/**
|
||||
@@ -160,7 +160,7 @@ class CollaborationService {
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace app
|
||||
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_NET_COLLABORATION_SERVICE_H_
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
namespace rom_svc = ::yaze::proto;
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
|
||||
namespace net {
|
||||
|
||||
RomServiceImpl::RomServiceImpl(
|
||||
@@ -204,7 +204,7 @@ grpc::Status RomServiceImpl::SetDialogue(
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace app
|
||||
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_WITH_GRPC
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "app/net/rom_version_manager.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
|
||||
namespace net {
|
||||
|
||||
#ifdef YAZE_WITH_GRPC
|
||||
@@ -58,7 +58,7 @@ class RomServiceImpl final : public proto::RomService::Service {
|
||||
~RomServiceImpl() override = default;
|
||||
|
||||
// Initialize with configuration
|
||||
void SetConfig(const Config& config) { config_ = config; }
|
||||
void SetConfig(const Config& config);
|
||||
|
||||
// =========================================================================
|
||||
// Basic ROM Operations
|
||||
@@ -165,7 +165,7 @@ class RomServiceImpl final : public proto::RomService::Service {
|
||||
#endif // YAZE_WITH_GRPC
|
||||
|
||||
} // namespace net
|
||||
} // namespace app
|
||||
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_NET_ROM_SERVICE_IMPL_H_
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#endif
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
|
||||
namespace net {
|
||||
|
||||
namespace {
|
||||
@@ -529,5 +529,5 @@ ProposalApprovalManager::GetProposalStatus(
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace app
|
||||
|
||||
} // namespace yaze
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#endif
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
|
||||
namespace net {
|
||||
|
||||
/**
|
||||
@@ -286,7 +286,7 @@ class ProposalApprovalManager {
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace app
|
||||
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_NET_ROM_VERSION_MANAGER_H_
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#endif
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
|
||||
namespace net {
|
||||
|
||||
#ifdef YAZE_WITH_JSON
|
||||
@@ -460,5 +460,5 @@ absl::Status WebSocketClient::SendRaw(const nlohmann::json& message) {
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace app
|
||||
|
||||
} // namespace yaze
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#endif
|
||||
|
||||
namespace yaze {
|
||||
namespace app {
|
||||
|
||||
namespace net {
|
||||
|
||||
/**
|
||||
@@ -208,7 +208,7 @@ class WebSocketClient {
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace app
|
||||
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_NET_WEBSOCKET_CLIENT_H_
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef YAZE_APP_CORE_PLATFORM_APP_DELEGATE_H
|
||||
#define YAZE_APP_CORE_PLATFORM_APP_DELEGATE_H
|
||||
#ifndef YAZE_APP_PLATFORM_APP_DELEGATE_H
|
||||
#define YAZE_APP_PLATFORM_APP_DELEGATE_H
|
||||
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
/* Apple OSX and iOS (Darwin). */
|
||||
@@ -61,4 +61,4 @@ int yaze_run_cocoa_app_delegate(const char *filename);
|
||||
|
||||
#endif // defined(__APPLE__) && defined(__MACH__)
|
||||
|
||||
#endif // YAZE_APP_CORE_PLATFORM_APP_DELEGATE_H
|
||||
#endif // YAZE_APP_PLATFORM_APP_DELEGATE_H
|
||||
@@ -1,5 +1,5 @@
|
||||
// AppDelegate.mm
|
||||
#import "app/core/platform/app_delegate.h"
|
||||
#import "app/platform/app_delegate.h"
|
||||
#import "app/core/controller.h"
|
||||
#import "util/file_util.h"
|
||||
#import "app/editor/editor.h"
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "app/core/platform/asset_loader.h"
|
||||
#include "app/platform/asset_loader.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "util/file_util.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace core {
|
||||
|
||||
|
||||
std::vector<std::filesystem::path> AssetLoader::GetSearchPaths(const std::string& relative_path) {
|
||||
std::vector<std::filesystem::path> search_paths;
|
||||
@@ -90,5 +90,5 @@ bool AssetLoader::AssetExists(const std::string& relative_path) {
|
||||
return FindAssetFile(relative_path).ok();
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
|
||||
} // namespace yaze
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef YAZE_APP_CORE_PLATFORM_ASSET_LOADER_H_
|
||||
#define YAZE_APP_CORE_PLATFORM_ASSET_LOADER_H_
|
||||
#ifndef YAZE_APP_PLATFORM_ASSET_LOADER_H_
|
||||
#define YAZE_APP_PLATFORM_ASSET_LOADER_H_
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "absl/status/statusor.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace core {
|
||||
|
||||
|
||||
/**
|
||||
* @class AssetLoader
|
||||
@@ -51,7 +51,7 @@ class AssetLoader {
|
||||
static bool AssetExists(const std::string& relative_path);
|
||||
};
|
||||
|
||||
} // namespace core
|
||||
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_CORE_PLATFORM_ASSET_LOADER_H_
|
||||
#endif // YAZE_APP_PLATFORM_ASSET_LOADER_H_
|
||||
@@ -22,7 +22,7 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
|
||||
|
||||
#include "app/core/platform/app_delegate.h"
|
||||
#include "app/platform/app_delegate.h"
|
||||
|
||||
namespace {
|
||||
static std::string selectedFile;
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "app/core/platform/font_loader.h"
|
||||
#include "app/platform/font_loader.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "util/macro.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace core {
|
||||
|
||||
static const char* KARLA_REGULAR = "Karla-Regular.ttf";
|
||||
static const char* ROBOTO_MEDIUM = "Roboto-Medium.ttf";
|
||||
@@ -138,5 +137,4 @@ void LoadSystemFonts() {
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace core
|
||||
} // namespace yaze
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef YAZE_APP_CORE_PLATFORM_FONTLOADER_H
|
||||
#define YAZE_APP_CORE_PLATFORM_FONTLOADER_H
|
||||
#ifndef YAZE_APP_PLATFORM_FONTLOADER_H
|
||||
#define YAZE_APP_PLATFORM_FONTLOADER_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace core {
|
||||
|
||||
|
||||
struct FontConfig {
|
||||
const char* font_path;
|
||||
@@ -28,7 +28,7 @@ absl::Status ReloadPackageFont(const FontConfig& config);
|
||||
|
||||
void LoadSystemFonts();
|
||||
|
||||
} // namespace core
|
||||
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_CORE_PLATFORM_FONTLOADER_H
|
||||
#endif // YAZE_APP_PLATFORM_FONTLOADER_H
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "app/core/platform/font_loader.h"
|
||||
#include "app/platform/font_loader.h"
|
||||
|
||||
#import <CoreText/CoreText.h>
|
||||
#include <TargetConditionals.h>
|
||||
@@ -8,13 +8,13 @@
|
||||
|
||||
#if TARGET_OS_IPHONE == 1 || TARGET_IPHONE_SIMULATOR == 1
|
||||
/* iOS */
|
||||
void yaze::core::LoadSystemFonts() {}
|
||||
void yaze::LoadSystemFonts() {}
|
||||
|
||||
#elif TARGET_OS_MAC == 1
|
||||
/* macOS */
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
void yaze::core::LoadSystemFonts() {
|
||||
void yaze::LoadSystemFonts() {
|
||||
NSArray *fontNames = @[ @"Helvetica", @"Times New Roman", @"Courier", @"Arial", @"Verdana" ];
|
||||
|
||||
for (NSString *fontName in fontNames) {
|
||||
@@ -105,10 +105,8 @@ absl::StatusOr<std::array<gfx::Bitmap, kNumLinkSheets>> LoadLinkGraphics(
|
||||
link_graphics[i].Create(gfx::kTilesheetWidth, gfx::kTilesheetHeight,
|
||||
gfx::kTilesheetDepth, link_sheet_8bpp);
|
||||
link_graphics[i].SetPalette(rom.palette_group().armors[0]);
|
||||
// TODO: Renderer refactor to use IRenderer or defer for later when GraphicsEditor is opened.
|
||||
// if (SDL_Renderer *renderer = Renderer::Get().renderer(); renderer != nullptr) {
|
||||
// Renderer::Get().RenderBitmap(&link_graphics[i]);
|
||||
// }
|
||||
// Texture creation is deferred until GraphicsEditor is opened and renderer is available.
|
||||
// The graphics will be queued for texturing when needed via Arena's deferred system.
|
||||
}
|
||||
return link_graphics;
|
||||
}
|
||||
|
||||
@@ -321,8 +321,9 @@ void Room::RenderRoomGraphics() {
|
||||
}
|
||||
|
||||
// CRITICAL: Recreate textures with the palette applied!
|
||||
core::Renderer::Get().RenderBitmap(&bg1_buffer_.bitmap());
|
||||
core::Renderer::Get().RenderBitmap(&bg2_buffer_.bitmap());
|
||||
// TODO: Queue texture for later rendering.
|
||||
// core::Renderer::Get().RenderBitmap(&bg1_buffer_.bitmap());
|
||||
// core::Renderer::Get().RenderBitmap(&bg2_buffer_.bitmap());
|
||||
}
|
||||
|
||||
void Room::RenderObjectsToBackground() {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gfx/tilemap.h"
|
||||
#include "app/gfx/backend/irenderer.h"
|
||||
#include "app/snes.h"
|
||||
#include "util/hex.h"
|
||||
|
||||
@@ -130,7 +131,8 @@ absl::Status LoadDungeonMapTile16(gfx::Tilemap &tile16_blockset, Rom &rom,
|
||||
}
|
||||
|
||||
tile16_blockset.atlas.SetPalette(*rom.mutable_dungeon_palette(3));
|
||||
core::Renderer::Get().RenderBitmap(&tile16_blockset.atlas);
|
||||
// TODO: Queue texture for later rendering.
|
||||
// core::Renderer::Get().RenderBitmap(&tile16_blockset.atlas);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
@@ -187,7 +189,8 @@ absl::Status LoadDungeonMapGfxFromBinary(Rom &rom,
|
||||
converted_bin.begin() + ((i + 1) * 0x1000));
|
||||
sheets[i] = gfx::Bitmap(128, 32, 8, gfx_sheets[i]);
|
||||
sheets[i].SetPalette(*rom.mutable_dungeon_palette(3));
|
||||
core::Renderer::Get().RenderBitmap(&sheets[i]);
|
||||
// TODO: Queue texture for later rendering.
|
||||
// core::Renderer::Get().RenderBitmap(&sheets[i]);
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "inventory.h"
|
||||
|
||||
#include "app/gfx/backend/irenderer.h"
|
||||
#include "app/core/window.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
@@ -8,8 +9,6 @@
|
||||
namespace yaze {
|
||||
namespace zelda3 {
|
||||
|
||||
using core::Renderer;
|
||||
|
||||
absl::Status Inventory::Create() {
|
||||
data_.reserve(256 * 256);
|
||||
for (int i = 0; i < 256 * 256; i++) {
|
||||
@@ -69,7 +68,8 @@ absl::Status Inventory::Create() {
|
||||
|
||||
bitmap_.Create(256, 256, 8, data_);
|
||||
bitmap_.SetPalette(palette_);
|
||||
Renderer::Get().RenderBitmap(&bitmap_);
|
||||
// TODO: Queue texture for later rendering.
|
||||
// Renderer::Get().RenderBitmap(&bitmap_);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
@@ -88,7 +88,8 @@ absl::Status Inventory::BuildTileset() {
|
||||
auto hud_pal_group = rom()->palette_group().hud;
|
||||
palette_ = hud_pal_group[0];
|
||||
tilesheets_bmp_.SetPalette(palette_);
|
||||
Renderer::Get().RenderBitmap(&tilesheets_bmp_);
|
||||
// TODO: Queue texture for later rendering.
|
||||
// Renderer::Get().RenderBitmap(&tilesheets_bmp_);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,38 @@ class Rom;
|
||||
namespace cli {
|
||||
namespace agent {
|
||||
|
||||
enum class ToolCallType {
|
||||
kUnknown,
|
||||
kResourceList,
|
||||
kResourceSearch,
|
||||
kDungeonListSprites,
|
||||
kDungeonDescribeRoom,
|
||||
// Overworld
|
||||
kOverworldFindTile,
|
||||
kOverworldDescribeMap,
|
||||
kOverworldListWarps,
|
||||
kOverworldListSprites,
|
||||
kOverworldGetEntrance,
|
||||
kOverworldTileStats,
|
||||
//
|
||||
kMessageList,
|
||||
kMessageRead,
|
||||
kMessageSearch,
|
||||
kGuiPlaceTile,
|
||||
kGuiClick,
|
||||
kGuiDiscover,
|
||||
kGuiScreenshot,
|
||||
kDialogueList,
|
||||
kDialogueRead,
|
||||
kDialogueSearch,
|
||||
kMusicList,
|
||||
kMusicInfo,
|
||||
kMusicTracks,
|
||||
kSpriteList,
|
||||
kSpriteProperties,
|
||||
kSpritePalette,
|
||||
};
|
||||
|
||||
class ToolDispatcher {
|
||||
public:
|
||||
ToolDispatcher() = default;
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include "app/core/controller.h"
|
||||
#include "app/core/platform/app_delegate.h"
|
||||
#include "app/core/platform/font_loader.h"
|
||||
#include "app/platform/app_delegate.h"
|
||||
#include "app/platform/font_loader.h"
|
||||
#include "app/core/window.h"
|
||||
#include "app/rom.h"
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
#undef main
|
||||
#endif
|
||||
|
||||
#include "app/core/platform/view_controller.h"
|
||||
#include "app/platform/view_controller.h"
|
||||
#include "imgui/backends/imgui_impl_sdl2.h"
|
||||
#include "imgui/backends/imgui_impl_sdlrenderer2.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
Reference in New Issue
Block a user