* chore(ci): add clang-format gate, CODEOWNERS and agent collaboration rules * refactor(modules): clear remaining back-edges and split the largest TUs Three known dependency back-edges are gone (module_depgraph.py KNOWN_BACK_EDGES is now empty; --check passes with 0 violations, 0 cycles): - window -> graphics: window queries the new IWindowSurfaceHost capability (common/WindowSurfaceHost.h) instead of holding a Graphics pointer; Window::setGraphics was removed and all call sites updated. - window -> image: Window::setIcon was replaced by setIconRGBA(const uint8_t*, w, h); the window no longer decodes ImageData. - scene -> graphics: pickScreenAt / collectFrustumIdsAt implementations moved to graphics/ScenePicking.cpp (graphics -> scene is a legal downward edge); scene now depends only on spatial. TU splits (pure moves, verified identical method sets and brace balance): - vulkan/Graphics.cpp (5118 lines) -> Graphics.cpp (lifecycle), GraphicsPipeline, Graphics2D, Graphics3D, GraphicsMesh + shared GraphicsInternal.h. - scene/Scene.cpp -> Scene.cpp (script layer / host lifecycle / serialization) + SceneNodes.cpp + shared SceneInternal.h. Verified with a real build (MSVC 19.51, prebuilt third-party): full build green; window.* and Scene.* suites pass, including scene picking/culling and GPU cases. * refactor(graphics): narrow Graphics interfaces and clear public-header leaks Step 4 — interface segregation: - New lean interfaces: IGraphics2D, IGraphics3D, IResourceFactory, IPostFX (pure virtuals, forward-declared types only; graphics/Color.h extracted from Canvas.h so modules that only need the color type stop pulling Canvas.h). - Graphics now implements all four facets; consumers can depend on the narrow interface they use instead of the 129-method god class. - Migrated voxel (VoxelWorld::drawVisible -> IGraphics3D*) and model3d (ModelRenderer::buildRenderable(s) -> IResourceFactory&). - Graphics.h slims from 24 sub-header includes to a handful of small type headers + forward declarations; the destructor moved out-of-line so unique_ptr effect members are destroyed where types are complete. Step 5 — header leaks (module_depgraph.py '*' marks: 14 -> 7): - map/TileLayer.h, particles/ParticleEmitter.h, avatar/AvatarInstance.h, animation/SpineAnim.h, map/TileSystem.h, model3d/ModelData.h, model3d/ModelRenderer.h, audio/Audio.h + Source.h, image/ImageData.h + Image.h, dialogue/Dialogue.h, procgen Palette/OutputSpec/TextureRecipe/ PbrMaterial -> forward declarations, includes moved to .cpp. - voxel/VoxelWorld.h: terrain sampler Pimpl'd (unique_ptr + out-of-line destructor), removing the procgen header dependency. - gpgpu/vulkan backend headers stop pulling graphics/vulkan/Graphics.h. - Remaining '*' marks are deliberate: tiny POD/enum headers (GridConfig, MapObject, BlendMode, Color), an internal backend header (GraphicsInternal.h -> filesystem), and lean interface references. Verified with a full clean build (MSVC) and tests: window/Scene/voxel/ RenderSystem/Quad/shader/TextureSampler/resourceLifetime pass; broad suite only shows the pre-existing PHYSFS-init filesystem-environment failures. * feat(resources): revive ResourceManager as unified IAssetReloader-backed cache * fix(resources): leak ResourceManager singleton to avoid exit-time teardown crashes Cached CPU resources may own third-party handles (FreeType faces, Assimp scenes, image decode handlers) whose libraries are torn down at process exit in an unspecified TU order; destroying cached entries from the singleton destructor can segfault at exit (seen on CI Windows in model3d.materialApi.objMtl). Keep the singleton alive until OS reclaim; explicit unload()/clear() still release entries during the run. * refactor(devtools): consume business modules through capability interfaces DevTools / cmdline no longer include business-module headers, so trimming scene / physics / procgen / particles / audio / ui / graphics no longer requires disabling devtools, and devtools can be switched off with -DEVENGINE_MODULE_DEVTOOLS=OFF. New capability interfaces in common/: - ISceneQuery (scene) - host/node inspection + mutation + transform sync - IRenderCapture (graphics) - render status, PNG capture, camera control, visible-entity snapshot, entity-ID mask, G-buffer PNGs - IPhysicsQuery (physics) - 2D world lifecycle + ray casts - IProcgenQuery (procgen) - recipe listing, map/mesh generation - IParticlesQuery (particles) - emitter status + one-shot emit - IAudioQuery (audio) - volume / stop-all - IEditorHost (ui) - declarative editor host (used by cmdline too) Providers register in their module constructors. DevTools rewires McpServer (render/scene/procgen/physics/particles/audio/host tools), SceneInspect (now delegates to ISceneQuery + IRenderCapture) and RenderVision (uses IRenderCapture::capturePngDataUrl); cmdline/Mcp.cpp uses IEditorHost. manifest: devtools DEPS graphics scene physics procgen particles audio ui image event filesystem -> event; cmdline DEPS filesystem ui -> filesystem. Verified: full build green; devtools.mcp.* tests pass; broad suite shows only the pre-existing PHYSFS-init filesystem-environment failures. * style: apply clang-format to changed lines after dev merge * fix(webgpu): include RenderControl/TextureSampler explicitly after Graphics.h slimming * fix(webgpu): provide newFont/print stubs to satisfy IGraphics2D/IResourceFactory vtables * fix(common): make ProcgenQuery.h self-sufficient (cstdint/utility for GCC)
239 lines
6.0 KiB
C++
239 lines
6.0 KiB
C++
/**
|
||
* Copyright (c) 2006-2021 LOVE Development Team
|
||
*
|
||
* This software is provided 'as-is', without any express or implied
|
||
* warranty. In no event will the authors be held liable for any damages
|
||
* arising from the use of this software.
|
||
*
|
||
* Permission is granted to anyone to use this software for any purpose,
|
||
* including commercial applications, and to alter it and redistribute it
|
||
* freely, subject to the following restrictions:
|
||
*
|
||
* 1. The origin of this software must not be misrepresented; you must not
|
||
* claim that you wrote the original software. If you use this software
|
||
* in a product, an acknowledgment in the product documentation would be
|
||
* appreciated but is not required.
|
||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||
* misrepresented as being the original software.
|
||
* 3. This notice may not be removed or altered from any source distribution.
|
||
**/
|
||
|
||
#include "Image.h"
|
||
#include "common/Exception.h"
|
||
#include "common/Resource.h"
|
||
#include "common/config.h"
|
||
|
||
#include "medialoader/image/PNGHandler.h"
|
||
#include "medialoader/image/STBHandler.h"
|
||
#include "medialoader/image/EXRHandler.h"
|
||
|
||
#include "medialoader/image/ddsHandler.h"
|
||
#include "medialoader/image/PVRHandler.h"
|
||
#include "medialoader/image/KTXHandler.h"
|
||
#include "medialoader/image/PKMHandler.h"
|
||
#include "medialoader/image/ASTCHandler.h"
|
||
|
||
#include <simplesquirrel/simplesquirrel.hpp>
|
||
|
||
namespace eve
|
||
{
|
||
namespace image
|
||
{
|
||
|
||
Module_IMPL(Image, new Image());
|
||
|
||
Image::Image()
|
||
{
|
||
using namespace medialoader;
|
||
|
||
float16Init(); // Makes sure half-float conversions can be used.
|
||
|
||
formatHandlers = {
|
||
new PNGHandler,
|
||
new STBHandler,
|
||
new EXRHandler,
|
||
new DDSHandler,
|
||
new PVRHandler,
|
||
new KTXHandler,
|
||
new PKMHandler,
|
||
new ASTCHandler,
|
||
};
|
||
}
|
||
|
||
Image::~Image()
|
||
{
|
||
// ImageData objects reference the FormatHandlers in our list, so we should
|
||
// release them instead of deleting them completely here.
|
||
|
||
// TODO: Check if this is necessary
|
||
// for (FormatHandler *handler : formatHandlers)
|
||
// handler->release();
|
||
}
|
||
|
||
|
||
ImageData *Image::newImageData(Data *data)
|
||
{
|
||
return new ImageData(data);
|
||
}
|
||
|
||
ImageData *Image::newImageDataFromFile(std::string path)
|
||
{
|
||
if (path.empty())
|
||
throw eve::Exception("Image::newImageDataFromFile: empty path");
|
||
|
||
eve::Resource *resource = eve::ResourceManager::getInstance().get(path);
|
||
if (!resource)
|
||
throw eve::Exception("Could not load image file: %s", path.c_str());
|
||
return static_cast<ImageData *>(resource);
|
||
}
|
||
|
||
ImageData *Image::newImageData(int width, int height, std::string format)
|
||
{
|
||
return new ImageData(width, height, format);
|
||
}
|
||
|
||
ImageData *Image::newImageData(int width, int height, std::string format, void *data, bool own)
|
||
{
|
||
return new ImageData(width, height, format, data, own);
|
||
}
|
||
|
||
|
||
bool Image::isCompressed(Data *data)
|
||
{
|
||
for (FormatHandler *handler : formatHandlers)
|
||
{
|
||
if (handler->canParseCompressed((const char*)data->getData(), data->getSize()))
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
const std::list<medialoader::FormatHandler *> &Image::getFormatHandlers() const
|
||
{
|
||
return formatHandlers;
|
||
}
|
||
|
||
ImageData *Image::newPastedImageData(ImageData *src, int sx, int sy, int w, int h)
|
||
{
|
||
ImageData *res = newImageData(w, h, src->getFormat());
|
||
try
|
||
{
|
||
res->paste(src, 0, 0, sx, sy, w, h);
|
||
}
|
||
catch (eve::Exception &)
|
||
{
|
||
// res->release();
|
||
throw;
|
||
}
|
||
return res;
|
||
}
|
||
|
||
std::vector<eve::ref<ImageData>> Image::newCubeFaces(ImageData *src)
|
||
{
|
||
// The faces array is always ordered +x, -x, +y, -y, +z, -z.
|
||
std::vector<eve::ref<ImageData>> faces;
|
||
|
||
int totalW = src->getWidth();
|
||
int totalH = src->getHeight();
|
||
|
||
if (totalW % 3 == 0 && totalH % 4 == 0 && totalW / 3 == totalH / 4)
|
||
{
|
||
// +y
|
||
// +z +x -z
|
||
// -y
|
||
// -x
|
||
|
||
int w = totalW / 3;
|
||
int h = totalH / 4;
|
||
|
||
faces.emplace_back(newPastedImageData(src, 1*w, 1*h, w, h));
|
||
faces.emplace_back(newPastedImageData(src, 1*w, 3*h, w, h));
|
||
faces.emplace_back(newPastedImageData(src, 1*w, 0*h, w, h));
|
||
faces.emplace_back(newPastedImageData(src, 1*w, 2*h, w, h));
|
||
faces.emplace_back(newPastedImageData(src, 0*w, 1*h, w, h));
|
||
faces.emplace_back(newPastedImageData(src, 2*w, 1*h, w, h));
|
||
}
|
||
else if (totalW % 4 == 0 && totalH % 3 == 0 && totalW / 4 == totalH / 3)
|
||
{
|
||
// +y
|
||
// -x +z +x -z
|
||
// -y
|
||
|
||
int w = totalW / 4;
|
||
int h = totalH / 3;
|
||
|
||
faces.emplace_back(newPastedImageData(src, 2*w, 1*h, w, h));
|
||
faces.emplace_back(newPastedImageData(src, 0*w, 1*h, w, h));
|
||
faces.emplace_back(newPastedImageData(src, 1*w, 0*h, w, h));
|
||
faces.emplace_back(newPastedImageData(src, 1*w, 2*h, w, h));
|
||
faces.emplace_back(newPastedImageData(src, 1*w, 1*h, w, h));
|
||
faces.emplace_back(newPastedImageData(src, 3*w, 1*h, w, h));
|
||
}
|
||
else if (totalH % 6 == 0 && totalW == totalH / 6)
|
||
{
|
||
// +x
|
||
// -x
|
||
// +y
|
||
// -y
|
||
// +z
|
||
// -z
|
||
|
||
int w = totalW;
|
||
int h = totalH / 6;
|
||
|
||
for (int i = 0; i < 6; i++)
|
||
faces.emplace_back(newPastedImageData(src, 0, i * h, w, h));
|
||
}
|
||
else if (totalW % 6 == 0 && totalW / 6 == totalH)
|
||
{
|
||
// +x -x +y -y +z -z
|
||
|
||
int w = totalW / 6;
|
||
int h = totalH;
|
||
|
||
for (int i = 0; i < 6; i++)
|
||
faces.emplace_back(newPastedImageData(src, i * w, 0, w, h));
|
||
}
|
||
else
|
||
throw eve::Exception("Unknown cubemap image dimensions!");
|
||
|
||
return faces;
|
||
}
|
||
|
||
std::vector<eve::ref<ImageData>> Image::newVolumeLayers(ImageData *src)
|
||
{
|
||
std::vector<eve::ref<ImageData>> layers;
|
||
|
||
int totalW = src->getWidth();
|
||
int totalH = src->getHeight();
|
||
|
||
if (totalW % totalH == 0)
|
||
{
|
||
for (int i = 0; i < totalW / totalH; i++)
|
||
layers.emplace_back(newPastedImageData(src, i * totalH, 0, totalH, totalH));
|
||
}
|
||
else if (totalH % totalW == 0)
|
||
{
|
||
for (int i = 0; i < totalH / totalW; i++)
|
||
layers.emplace_back(newPastedImageData(src, 0, i * totalW, totalW, totalW));
|
||
}
|
||
else
|
||
throw eve::Exception("Cannot extract volume layers from source ImageData.");
|
||
|
||
return layers;
|
||
}
|
||
|
||
void Image::expose(ssq::Table &table) {
|
||
auto cls = table.addClass(name, Image::create, false);
|
||
expose(cls);
|
||
}
|
||
|
||
void Image::expose(ssq::Class &cls) {
|
||
cls.addFunc("getName", &Image::getName);
|
||
cls.addFunc("newImageDataFromFile", &Image::newImageDataFromFile);
|
||
}
|
||
|
||
} // image
|
||
} // eve
|