Initial commit

This commit is contained in:
2025-12-17 16:47:48 +00:00
commit 13813f3363
4964 changed files with 1079753 additions and 0 deletions

213
engine/gfx/CMakeLists.txt Normal file
View File

@@ -0,0 +1,213 @@
add_library(Graphics STATIC)
set(SOURCES
# GfxBase
src/Base/Adorn.cpp
src/Base/Adorn.hpp
src/Base/AdornBillboarder.cpp
src/Base/AdornBillboarder.hpp
src/Base/AdornBillboarder2D.cpp
src/Base/AdornBillboarder2D.hpp
src/Base/AdornSurface.cpp
src/Base/AdornSurface.hpp
src/Base/AsyncResult.hpp
src/Base/FileMeshData.cpp
src/Base/FileMeshData.hpp
src/Base/FrameRateManager.cpp
src/Base/FrameRateManager.hpp
src/Base/GfxPart.cpp
src/Base/GfxPart.hpp
src/Base/IAdornable.hpp
src/Base/IAdornableCollector.cpp
src/Base/IAdornableCollector.hpp
src/Base/Image.hpp
src/Base/MeshFileStructs.hpp
src/Base/MeshGen.hpp
src/Base/ObjLoader.hpp
src/Base/Part.hpp
src/Base/PartIdentifier.cpp
src/Base/PartIdentifier.hpp
src/Base/RenderCaps.cpp
src/Base/RenderCaps.hpp
src/Base/RenderSettings.cpp
src/Base/RenderSettings.hpp
src/Base/RenderStats.cpp
src/Base/RenderStats.hpp
src/Base/TextureProxyBase.hpp
src/Base/Type.hpp
src/Base/Typesetter.hpp
src/Base/ViewBase.cpp
src/Base/ViewBase.hpp
src/Base/ViewportBillboarder.cpp
src/Base/ViewportBillboarder.hpp
# GfxCore
src/Core/Device.cpp
src/Core/Device.hpp
src/Core/DeviceCreate.cpp
src/Core/Framebuffer.cpp
src/Core/Framebuffer.hpp
src/Core/Geometry.cpp
src/Core/Geometry.hpp
src/Core/Pix.cpp
src/Core/Pix.hpp
src/Core/Resource.cpp
src/Core/Resource.hpp
src/Core/Shader.cpp
src/Core/Shader.hpp
src/Core/States.cpp
src/Core/States.hpp
src/Core/Texture.cpp
src/Core/Texture.hpp
# GfxRender
src/Render/AdornRender.cpp
src/Render/AdornRender.hpp
src/Render/CullableSceneNode.cpp
src/Render/CullableSceneNode.hpp
src/Render/CustomEmitter.cpp
src/Render/CustomEmitter.hpp
src/Render/Emitter.cpp
src/Render/Emitter.hpp
src/Render/EmitterShared.hpp
src/Render/EnvMap.cpp
src/Render/EnvMap.hpp
src/Render/ExplosionEmitter.cpp
src/Render/ExplosionEmitter.hpp
src/Render/FastCluster.cpp
src/Render/FastCluster.hpp
src/Render/GeometryGenerator.cpp
src/Render/GeometryGenerator.hpp
src/Render/GlobalShaderData.cpp
src/Render/GlobalShaderData.hpp
src/Render/Image.cpp
src/Render/Image.hpp
src/Render/ImageInfo.hpp
src/Render/LightGrid.cpp
src/Render/LightGrid.hpp
src/Render/LightObject.cpp
src/Render/LightObject.hpp
src/Render/Material.cpp
src/Render/Material.hpp
src/Render/MaterialGenerator.cpp
src/Render/MaterialGenerator.hpp
src/Render/MegaCluster.cpp
src/Render/MegaCluster.hpp
src/Render/ObjectExporter.cpp
src/Render/ObjectExporter.hpp
src/Render/ParticleEmitter.cpp
src/Render/ParticleEmitter.hpp
src/Render/RenderCamera.cpp
src/Render/RenderCamera.hpp
src/Render/RenderNode.cpp
src/Render/RenderNode.hpp
src/Render/RenderQueue.cpp
src/Render/RenderQueue.hpp
src/Render/RenderView.cpp
src/Render/RenderView.hpp
src/Render/SceneManager.cpp
src/Render/SceneManager.hpp
src/Render/SceneUpdater.cpp
src/Render/SceneUpdater.hpp
src/Render/ScreenSpaceEffect.cpp
src/Render/ScreenSpaceEffect.hpp
src/Render/ShaderManager.cpp
src/Render/ShaderManager.hpp
src/Render/Sky.cpp
src/Render/Sky.hpp
src/Render/SmoothCluster.cpp
src/Render/SmoothCluster.hpp
src/Render/SpatialGrid.hpp
src/Render/SpatialHashedScene.cpp
src/Render/SpatialHashedScene.hpp
src/Render/SSAO.cpp
src/Render/SSAO.hpp
src/Render/SuperCluster.cpp
src/Render/SuperCluster.hpp
src/Render/TextureAtlas.cpp
src/Render/TextureAtlas.hpp
src/Render/TextureCompositor.cpp
src/Render/TextureCompositor.hpp
src/Render/TextureManager.cpp
src/Render/TextureManager.hpp
src/Render/TextureRef.cpp
src/Render/TextureRef.hpp
src/Render/TypesetterDynamic.cpp
src/Render/TypesetterDynamic.hpp
src/Render/Util.hpp
src/Render/VertexStreamer.cpp
src/Render/VertexStreamer.hpp
src/Render/VisualEngine.cpp
src/Render/VisualEngine.hpp
src/Render/Water.cpp
src/Render/Water.hpp
)
# BGFX API integration
list(APPEND SOURCES
src/API/BGFX/DeviceContextBGFX.cpp
src/API/BGFX/DeviceBGFX.cpp
src/API/BGFX/DeviceBGFX.hpp
src/API/BGFX/FramebufferBGFX.cpp
src/API/BGFX/FramebufferBGFX.hpp
src/API/BGFX/GeometryBGFX.cpp
src/API/BGFX/GeometryBGFX.hpp
src/API/BGFX/HeadersBGFX.hpp
src/API/BGFX/ShaderBGFX.cpp
src/API/BGFX/ShaderBGFX.hpp
src/API/BGFX/TextureBGFX.cpp
src/API/BGFX/TextureBGFX.hpp
)
# OpenGL API integration
list(APPEND SOURCES
src/API/GL/ContextGL.hpp
src/API/GL/DeviceContextGL.cpp
src/API/GL/DeviceGL.cpp
src/API/GL/DeviceGL.hpp
src/API/GL/FramebufferGL.cpp
src/API/GL/FramebufferGL.hpp
src/API/GL/GeometryGL.cpp
src/API/GL/GeometryGL.hpp
src/API/GL/HeadersGL.hpp
src/API/GL/ShaderGL.cpp
src/API/GL/ShaderGL.hpp
src/API/GL/TextureGL.cpp
src/API/GL/TextureGL.hpp
src/API/GL/glad/include/glad/gl.h
src/API/GL/glad/src/gl.c
)
if(AYA_OS_WINDOWS)
list(APPEND SOURCES
src/API/GL/glad/include/glad/wgl.h
src/API/GL/glad/src/wgl.c
src/API/GL/ContextGLWin32.cpp
)
elseif(AYA_OS_LINUX)
list(APPEND SOURCES
src/API/GL/glad/include/EGL/eglplatform.h
src/API/GL/glad/include/glad/egl.h
src/API/GL/glad/src/egl.c
src/API/GL/ContextEGL.cpp
)
elseif(AYA_OS_MACOS)
list(APPEND SOURCES src/API/GL/ContextGLMac.mm)
elseif(AYA_OS_ANDROID)
list(APPEND SOURCES src/API/GL/ContextGLAndroid.cpp)
endif()
target_sources(Graphics PRIVATE ${SOURCES})
target_include_directories(Graphics
PUBLIC
${ENGINE_DIR}/3d/src
src
PRIVATE
src/API/GL/glad/include
${THIRD_PARTY_DIR}/BulletPhysics/src
${ENGINE_DIR}/app/src
${ENGINE_DIR}/core/src
)
target_link_libraries(Graphics $<TARGET_OBJECTS:Core> $<TARGET_OBJECTS:3D>)

View File

@@ -0,0 +1,314 @@
#include "DeviceBGFX.hpp"
#include "GeometryBGFX.hpp"
#include "ShaderBGFX.hpp"
#include "TextureBGFX.hpp"
#include "FramebufferBGFX.hpp"
#include "Profiler.hpp"
#include "ImGui.hpp"
#include <bgfx/bgfx.h>
#include <bgfx/platform.h>
#include <sstream>
#ifdef __linux
#include <wayland-client.h>
#include <X11/Xlib.h>
#endif
LOGGROUP(Graphics)
FASTFLAGVARIABLE(DebugGraphicsBGFX, false)
namespace Aya
{
namespace Graphics
{
static DeviceCapsBGFX createDeviceCaps()
{
const bgfx::Caps* bgfxCaps = bgfx::getCaps();
DeviceCapsBGFX caps;
caps.caps = bgfxCaps;
caps.supportsFramebuffer = true;
caps.supportsShaders = true;
caps.supportsFFP = false;
caps.supportsStencil = true;
caps.supportsIndex32 = bgfxCaps->supported & BGFX_CAPS_INDEX32;
caps.supportsTextureDXT = (bgfxCaps->formats[bgfx::TextureFormat::BC1] & BGFX_CAPS_FORMAT_TEXTURE_2D) != 0;
caps.supportsTexturePVR = (bgfxCaps->formats[bgfx::TextureFormat::PTC12] & BGFX_CAPS_FORMAT_TEXTURE_2D) != 0;
caps.supportsTextureHalfFloat = (bgfxCaps->formats[bgfx::TextureFormat::R16F] & BGFX_CAPS_FORMAT_TEXTURE_2D) != 0;
caps.supportsTexture3D = (bgfxCaps->supported & BGFX_CAPS_TEXTURE_3D) != 0;
caps.supportsTextureNPOT = true;
caps.supportsTextureETC1 = (bgfxCaps->formats[bgfx::TextureFormat::ETC1] & BGFX_CAPS_FORMAT_TEXTURE_2D) != 0;
caps.supportsTexturePartialMipChain = true;
caps.maxDrawBuffers = bgfxCaps->limits.maxFBAttachments;
caps.maxSamples = 1; // BGFX doesn't expose max MSAA samples directly
caps.maxTextureSize = bgfxCaps->limits.maxTextureSize;
caps.maxTextureUnits = bgfxCaps->limits.maxTextureSamplers;
caps.colorOrderBGR = bgfxCaps->rendererType == bgfx::RendererType::Direct3D11 || bgfxCaps->rendererType == bgfx::RendererType::Direct3D12;
caps.requiresRenderTargetFlipping =
bgfxCaps->rendererType == bgfx::RendererType::OpenGL || bgfxCaps->rendererType == bgfx::RendererType::OpenGLES;
caps.retina = false;
caps.supportsCompute = (bgfxCaps->supported & BGFX_CAPS_COMPUTE) != 0;
caps.supportsInstancing = (bgfxCaps->supported & BGFX_CAPS_INSTANCING) != 0;
caps.supportsTextureBlits = (bgfxCaps->supported & BGFX_CAPS_TEXTURE_BLIT) != 0;
caps.supportsTextureReadBack = (bgfxCaps->supported & BGFX_CAPS_TEXTURE_READ_BACK) != 0;
caps.supportsOcclusionQuery = (bgfxCaps->supported & BGFX_CAPS_OCCLUSION_QUERY) != 0;
return caps;
}
enum class DisplayBackend
{
Wayland,
X11,
Unknown
};
static DisplayBackend detectDisplayBackend()
{
const char* waylandDisplay = std::getenv("WAYLAND_DISPLAY");
const char* x11Display = std::getenv("DISPLAY");
if (waylandDisplay && waylandDisplay[0])
return DisplayBackend::Wayland;
if (x11Display && x11Display[0])
return DisplayBackend::X11;
return DisplayBackend::Unknown;
}
DeviceBGFX::DeviceBGFX(void* windowHandle, void* displayHandle)
: width(0)
, height(0)
, frameNumber(0)
{
bgfx::Init init;
init.type = bgfx::RendererType::Count; // Auto-select
init.platformData.nwh = windowHandle;
#ifdef __linux__
DisplayBackend backend = detectDisplayBackend();
if (backend == DisplayBackend::Wayland)
{
init.platformData.ndt = static_cast<wl_display*>(displayHandle);
init.platformData.type = bgfx::NativeWindowHandleType::Wayland;
}
else if (backend == DisplayBackend::X11)
{
init.platformData.ndt = static_cast<::Display*>(displayHandle);
init.platformData.type = bgfx::NativeWindowHandleType::Default; // X11 is default on Linux
}
#endif
if (!bgfx::init(init))
{
throw Aya::runtime_error("Failed to initialize BGFX");
}
rendererType = bgfx::getCaps()->rendererType;
caps = createDeviceCaps();
const char* rendererName = bgfx::getRendererName(rendererType);
FASTLOGS(FLog::Graphics, "BGFX Renderer: %s", rendererName);
caps.dumpToFLog(FLog::Graphics);
FASTLOG5(FLog::Graphics, "Caps: Compute %d Instancing %d TextureBlits %d TextureReadBack %d OcclusionQuery %d", caps.supportsCompute,
caps.supportsInstancing, caps.supportsTextureBlits, caps.supportsTextureReadBack, caps.supportsOcclusionQuery);
immediateContext.reset(new DeviceContextBGFX(this));
mainFramebuffer.reset(new FramebufferBGFX(this, BGFX_INVALID_HANDLE));
bgfx::setDebug(FFlag::DebugGraphicsBGFX ? BGFX_DEBUG_TEXT : BGFX_DEBUG_NONE);
#if defined(_WIN32) || defined(__linux__)
Profiler::gpuInit(0);
#endif
ImGui::gpuInit();
}
DeviceBGFX::~DeviceBGFX()
{
#if defined(_WIN32) || defined(__linux__)
Profiler::gpuShutdown();
#endif
ImGui::gpuShutdown();
immediateContext.reset();
mainFramebuffer.reset();
bgfx::shutdown();
}
void DeviceBGFX::resize(int w, int h)
{
width = w;
height = h;
bgfx::reset(width, height, BGFX_RESET_VSYNC);
}
std::string DeviceBGFX::getAPIName()
{
return bgfx::getRendererName(rendererType);
}
std::string DeviceBGFX::getFeatureLevel()
{
std::ostringstream oss;
oss << bgfx::getRendererName(rendererType);
return oss.str();
}
bool DeviceBGFX::validate()
{
// BGFX handles window resizing internally
return true;
}
DeviceContext* DeviceBGFX::beginFrame()
{
immediateContext->setCurrentView(0);
immediateContext->bindFramebuffer(mainFramebuffer.get());
immediateContext->clearStates();
return immediateContext.get();
}
void DeviceBGFX::endFrame()
{
bgfx::frame();
frameNumber++;
}
Framebuffer* DeviceBGFX::getMainFramebuffer()
{
return mainFramebuffer.get();
}
void DeviceBGFX::defineGlobalConstants(size_t dataSize, const std::vector<ShaderGlobalConstant>& constants)
{
AYAASSERT(globalConstants.empty());
AYAASSERT(!constants.empty());
globalConstants = constants;
immediateContext->defineGlobalConstants(dataSize);
}
std::string DeviceBGFX::createShaderSource(
const std::string& path, const std::string& defines, boost::function<std::string(const std::string&)> fileCallback)
{
return fileCallback(path);
}
std::vector<char> DeviceBGFX::createShaderBytecode(const std::string& source, const std::string& target, const std::string& entrypoint)
{
AYAASSERT(false && "BGFX shaders must be pre-compiled");
return std::vector<char>();
}
std::string DeviceBGFX::getShadingLanguage()
{
switch (rendererType)
{
case bgfx::RendererType::Direct3D11:
return "dx12";
case bgfx::RendererType::Direct3D12:
return "dx11";
case bgfx::RendererType::Metal:
return "metal";
case bgfx::RendererType::OpenGL:
return "glsl";
case bgfx::RendererType::OpenGLES:
return "glsles";
case bgfx::RendererType::Vulkan:
return "spirv";
default:
return "unknown";
}
}
shared_ptr<VertexShader> DeviceBGFX::createVertexShader(const std::vector<char>& bytecode)
{
return shared_ptr<VertexShader>(new VertexShaderBGFX(this, bytecode));
}
shared_ptr<FragmentShader> DeviceBGFX::createFragmentShader(const std::vector<char>& bytecode)
{
return shared_ptr<FragmentShader>(new FragmentShaderBGFX(this, bytecode));
}
shared_ptr<ShaderProgram> DeviceBGFX::createShaderProgram(
const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader)
{
return shared_ptr<ShaderProgram>(new ShaderProgramBGFX(this, vertexShader, fragmentShader));
}
shared_ptr<ShaderProgram> DeviceBGFX::createShaderProgramFFP()
{
throw Aya::runtime_error("No FFP support in BGFX");
}
shared_ptr<VertexBuffer> DeviceBGFX::createVertexBuffer(size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage)
{
return shared_ptr<VertexBuffer>(new VertexBufferBGFX(this, elementSize, elementCount, usage));
}
shared_ptr<IndexBuffer> DeviceBGFX::createIndexBuffer(size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage)
{
return shared_ptr<IndexBuffer>(new IndexBufferBGFX(this, elementSize, elementCount, usage));
}
shared_ptr<VertexLayout> DeviceBGFX::createVertexLayout(const std::vector<VertexLayout::Element>& elements)
{
return shared_ptr<VertexLayout>(new VertexLayoutBGFX(this, elements));
}
shared_ptr<Texture> DeviceBGFX::createTexture(Texture::Type type, Texture::Format format, unsigned int width, unsigned int height, unsigned int depth,
unsigned int mipLevels, Texture::Usage usage)
{
return shared_ptr<Texture>(new TextureBGFX(this, type, format, width, height, depth, mipLevels, usage));
}
shared_ptr<Renderbuffer> DeviceBGFX::createRenderbuffer(Texture::Format format, unsigned int width, unsigned int height, unsigned int samples)
{
return shared_ptr<Renderbuffer>(new RenderbufferBGFX(this, format, width, height, samples));
}
shared_ptr<Geometry> DeviceBGFX::createGeometryImpl(const shared_ptr<VertexLayout>& layout,
const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers, const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex)
{
return shared_ptr<Geometry>(new GeometryBGFX(this, layout, vertexBuffers, indexBuffer, baseVertexIndex));
}
shared_ptr<Framebuffer> DeviceBGFX::createFramebufferImpl(const std::vector<shared_ptr<Renderbuffer>>& color, const shared_ptr<Renderbuffer>& depth)
{
return shared_ptr<Framebuffer>(new FramebufferBGFX(this, color, depth));
}
DeviceStats DeviceBGFX::getStatistics() const
{
DeviceStats result = {};
const bgfx::Stats* stats = bgfx::getStats();
result.gpuFrameTime = static_cast<float>(stats->gpuTimeEnd - stats->gpuTimeBegin) / stats->gpuTimerFreq * 1000.0f;
return result;
}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,195 @@
#pragma once
#include "Core/Device.hpp"
#include "Core/States.hpp"
#include <bgfx/bgfx.h>
namespace Aya
{
namespace Graphics
{
class FramebufferBGFX;
class ShaderProgramBGFX;
class TextureBGFX;
class DeviceBGFX;
struct DeviceCapsBGFX : DeviceCaps
{
const bgfx::Caps* caps;
bool supportsCompute;
bool supportsInstancing;
bool supportsTextureBlits;
bool supportsTextureReadBack;
bool supportsOcclusionQuery;
};
class DeviceContextBGFX : public DeviceContext
{
public:
DeviceContextBGFX(DeviceBGFX* dev);
~DeviceContextBGFX();
void defineGlobalConstants(size_t dataSize);
void clearStates();
void invalidateCachedProgram();
void invalidateCachedTexture(Texture* texture);
void invalidateCachedTextureStage(unsigned int stage);
void handleStencilState();
virtual void setDefaultAnisotropy(unsigned int value);
virtual void updateGlobalConstants(const void* data, size_t dataSize);
virtual void bindFramebuffer(Framebuffer* buffer);
virtual void clearFramebuffer(unsigned int mask, const float color[4], float depth, unsigned int stencil);
virtual void copyFramebuffer(Framebuffer* buffer, Texture* texture, int xOffset, int yOffset);
virtual void resolveFramebuffer(Framebuffer* msaaBuffer, Framebuffer* buffer, unsigned int mask);
virtual void discardFramebuffer(Framebuffer* buffer, unsigned int mask);
virtual void bindProgram(ShaderProgram* program);
virtual void setWorldTransforms4x3(const float* data, size_t matrixCount);
virtual void setConstant(int handle, const float* data, size_t vectorCount);
virtual void bindTexture(unsigned int stage, Texture* texture, const SamplerState& state);
virtual void setRasterizerState(const RasterizerState& state);
virtual void setBlendState(const BlendState& state);
virtual void setDepthState(const DepthState& state);
virtual void drawImpl(Geometry* geometry, Geometry::Primitive primitive, unsigned int offset, unsigned int count, unsigned int indexRangeBegin,
unsigned int indexRangeEnd);
virtual void pushDebugMarkerGroup(const char* text);
virtual void popDebugMarkerGroup();
virtual void setDebugMarker(const char* text);
bgfx::ViewId getCurrentView() const
{
return currentView;
}
void setCurrentView(bgfx::ViewId view)
{
currentView = view;
}
private:
std::vector<char> globalData;
bgfx::UniformHandle globalUniform;
unsigned int globalDataVersion;
unsigned int defaultAnisotropy;
ShaderProgramBGFX* cachedProgram;
TextureBGFX* cachedTextures[16];
// Sampler uniforms for each texture stage
bgfx::UniformHandle samplerUniforms[16];
RasterizerState cachedRasterizerState;
BlendState cachedBlendState;
DepthState cachedDepthState;
uint64_t cachedStateFlags;
bgfx::ViewId currentView;
DeviceBGFX* device;
};
class DeviceBGFX : public Device
{
public:
DeviceBGFX(void* windowHandle, void* displayHandle);
~DeviceBGFX();
virtual bool validate();
virtual void resize(int w, int h);
virtual DeviceContext* beginFrame();
virtual void endFrame();
virtual Framebuffer* getMainFramebuffer();
virtual void defineGlobalConstants(size_t dataSize, const std::vector<ShaderGlobalConstant>& constants);
virtual std::string getAPIName();
virtual std::string getFeatureLevel();
virtual std::string getShadingLanguage();
virtual std::string createShaderSource(
const std::string& path, const std::string& defines, boost::function<std::string(const std::string&)> fileCallback);
virtual std::vector<char> createShaderBytecode(const std::string& source, const std::string& target, const std::string& entrypoint);
virtual shared_ptr<VertexShader> createVertexShader(const std::vector<char>& bytecode);
virtual shared_ptr<FragmentShader> createFragmentShader(const std::vector<char>& bytecode);
virtual shared_ptr<ShaderProgram> createShaderProgram(
const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader);
virtual shared_ptr<ShaderProgram> createShaderProgramFFP();
virtual shared_ptr<VertexBuffer> createVertexBuffer(size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage);
virtual shared_ptr<IndexBuffer> createIndexBuffer(size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage);
virtual shared_ptr<VertexLayout> createVertexLayout(const std::vector<VertexLayout::Element>& elements);
virtual shared_ptr<Texture> createTexture(Texture::Type type, Texture::Format format, unsigned int width, unsigned int height, unsigned int depth,
unsigned int mipLevels, Texture::Usage usage);
virtual shared_ptr<Renderbuffer> createRenderbuffer(Texture::Format format, unsigned int width, unsigned int height, unsigned int samples);
virtual shared_ptr<Geometry> createGeometryImpl(const shared_ptr<VertexLayout>& layout,
const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers, const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex);
virtual shared_ptr<Framebuffer> createFramebufferImpl(const std::vector<shared_ptr<Renderbuffer>>& color, const shared_ptr<Renderbuffer>& depth);
virtual const DeviceCaps& getCaps() const
{
return caps;
}
virtual DeviceStats getStatistics() const;
DeviceContextBGFX* getImmediateContextBGFX()
{
return immediateContext.get();
}
const DeviceCapsBGFX& getCapsBGFX() const
{
return caps;
}
const std::vector<ShaderGlobalConstant>& getGlobalConstants() const
{
return globalConstants;
}
bgfx::RendererType::Enum getRendererType() const
{
return rendererType;
}
private:
DeviceCapsBGFX caps;
scoped_ptr<DeviceContextBGFX> immediateContext;
scoped_ptr<FramebufferBGFX> mainFramebuffer;
std::vector<ShaderGlobalConstant> globalConstants;
bgfx::RendererType::Enum rendererType;
uint32_t width;
uint32_t height;
uint32_t frameNumber;
float gpuTime;
};
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,552 @@
#include "DeviceBGFX.hpp"
#include "GeometryBGFX.hpp"
#include "ShaderBGFX.hpp"
#include "TextureBGFX.hpp"
#include "FramebufferBGFX.hpp"
#include <bgfx/bgfx.h>
#ifdef AYA_OS_WINDOWS
#include <Windows.h>
#endif
namespace Aya
{
namespace Graphics
{
// Convert RasterizerState cull mode to BGFX state flags
static uint64_t getCullStateFlags(RasterizerState::CullMode mode)
{
switch (mode)
{
case RasterizerState::Cull_None:
return BGFX_STATE_CULL_CW | BGFX_STATE_CULL_CCW; // Disable culling
case RasterizerState::Cull_Back:
return BGFX_STATE_CULL_CW;
case RasterizerState::Cull_Front:
return BGFX_STATE_CULL_CCW;
default:
return 0;
}
}
// Convert BlendState factors to BGFX blend equation
static uint64_t getBlendStateFlags(const BlendState& state)
{
if (!state.blendingNeeded())
return 0;
static const uint64_t blendFactorsBGFX[BlendState::Factor_Count] = {
BGFX_STATE_BLEND_ONE, // Factor_One
BGFX_STATE_BLEND_ZERO, // Factor_Zero
BGFX_STATE_BLEND_DST_COLOR, // Factor_DstColor
BGFX_STATE_BLEND_SRC_ALPHA, // Factor_SrcAlpha
BGFX_STATE_BLEND_INV_SRC_ALPHA, // Factor_InvSrcAlpha
BGFX_STATE_BLEND_DST_ALPHA, // Factor_DstAlpha
BGFX_STATE_BLEND_INV_DST_ALPHA // Factor_InvDstAlpha
};
uint64_t srcBlend = blendFactorsBGFX[state.getColorSrc()];
uint64_t dstBlend = blendFactorsBGFX[state.getColorDst()];
uint64_t flags = BGFX_STATE_BLEND_FUNC(srcBlend, dstBlend);
if (state.separateAlphaBlend())
{
uint64_t srcAlpha = blendFactorsBGFX[state.getAlphaSrc()];
uint64_t dstAlpha = blendFactorsBGFX[state.getAlphaDst()];
flags = BGFX_STATE_BLEND_FUNC_SEPARATE(srcBlend, dstBlend, srcAlpha, dstAlpha);
}
return flags;
}
// Convert DepthState to BGFX state flags
static uint64_t getDepthStateFlags(const DepthState& state)
{
uint64_t flags = 0;
if (state.getFunction() != DepthState::Function_None)
{
switch (state.getFunction())
{
case DepthState::Function_Always:
flags |= BGFX_STATE_DEPTH_TEST_ALWAYS;
break;
case DepthState::Function_Less:
flags |= BGFX_STATE_DEPTH_TEST_LESS;
break;
case DepthState::Function_LessEqual:
flags |= BGFX_STATE_DEPTH_TEST_LEQUAL;
break;
default:
break;
}
}
if (state.getWrite())
{
flags |= BGFX_STATE_WRITE_Z;
}
return flags;
}
// Convert color mask to BGFX state flags
static uint64_t getColorMaskFlags(unsigned int colorMask)
{
uint64_t flags = 0;
if (colorMask & BlendState::Color_R)
flags |= BGFX_STATE_WRITE_R;
if (colorMask & BlendState::Color_G)
flags |= BGFX_STATE_WRITE_G;
if (colorMask & BlendState::Color_B)
flags |= BGFX_STATE_WRITE_B;
if (colorMask & BlendState::Color_A)
flags |= BGFX_STATE_WRITE_A;
return flags;
}
DeviceContextBGFX::DeviceContextBGFX(DeviceBGFX* dev)
: globalDataVersion(0)
, device(dev)
, defaultAnisotropy(1)
, cachedProgram(nullptr)
, cachedRasterizerState(RasterizerState::Cull_None)
, cachedBlendState(BlendState::Mode_None)
, cachedDepthState(DepthState::Function_Always, false)
, cachedStateFlags(0)
, currentView(0)
{
globalUniform = BGFX_INVALID_HANDLE;
for (size_t i = 0; i < ARRAYSIZE(cachedTextures); ++i)
{
cachedTextures[i] = nullptr;
// Create sampler uniforms for each texture stage
char uniformName[32];
snprintf(uniformName, sizeof(uniformName), "s_texColor%d", (int)i);
samplerUniforms[i] = bgfx::createUniform(uniformName, bgfx::UniformType::Sampler);
}
}
DeviceContextBGFX::~DeviceContextBGFX()
{
if (bgfx::isValid(globalUniform))
{
bgfx::destroy(globalUniform);
globalUniform = BGFX_INVALID_HANDLE;
}
// Destroy sampler uniforms
for (size_t i = 0; i < ARRAYSIZE(samplerUniforms); ++i)
{
if (bgfx::isValid(samplerUniforms[i]))
{
bgfx::destroy(samplerUniforms[i]);
samplerUniforms[i] = BGFX_INVALID_HANDLE;
}
}
}
void DeviceContextBGFX::clearStates()
{
// Clear program cache
cachedProgram = nullptr;
// Clear texture cache
for (size_t i = 0; i < ARRAYSIZE(cachedTextures); ++i)
cachedTextures[i] = nullptr;
// Clear states to invalid values to guarantee a cache miss on the next setup
cachedRasterizerState = RasterizerState(RasterizerState::Cull_Count);
cachedBlendState = BlendState(BlendState::Mode_Count);
cachedDepthState = DepthState(DepthState::Function_Count, false);
cachedStateFlags = 0;
}
void DeviceContextBGFX::invalidateCachedProgram()
{
cachedProgram = nullptr;
}
void DeviceContextBGFX::invalidateCachedTexture(Texture* texture)
{
TextureBGFX* textureBGFX = static_cast<TextureBGFX*>(texture);
for (unsigned int stage = 0; stage < ARRAYSIZE(cachedTextures); ++stage)
if (cachedTextures[stage] == textureBGFX)
cachedTextures[stage] = nullptr;
}
void DeviceContextBGFX::invalidateCachedTextureStage(unsigned int stage)
{
AYAASSERT(stage < ARRAYSIZE(cachedTextures));
cachedTextures[stage] = nullptr;
}
void DeviceContextBGFX::defineGlobalConstants(size_t dataSize)
{
AYAASSERT(globalData.empty());
AYAASSERT(dataSize > 0);
globalData.resize(dataSize);
// Create a uniform buffer for global constants
char uniformName[256];
snprintf(uniformName, sizeof(uniformName), "u_globals");
globalUniform = bgfx::createUniform(uniformName, bgfx::UniformType::Vec4, (dataSize + 15) / 16);
}
void DeviceContextBGFX::setDefaultAnisotropy(unsigned int value)
{
defaultAnisotropy = value;
}
void DeviceContextBGFX::updateGlobalConstants(const void* data, size_t dataSize)
{
AYAASSERT(dataSize == globalData.size());
memcpy(&globalData[0], data, dataSize);
globalDataVersion++;
// Update the uniform buffer
if (bgfx::isValid(globalUniform))
{
bgfx::setUniform(globalUniform, &globalData[0], (dataSize + 15) / 16);
}
}
void DeviceContextBGFX::bindFramebuffer(Framebuffer* buffer)
{
FramebufferBGFX* framebufferBGFX = static_cast<FramebufferBGFX*>(buffer);
// Set the framebuffer for the current view
bgfx::setViewFrameBuffer(currentView, framebufferBGFX->getHandle());
bgfx::setViewRect(currentView, 0, 0, uint16_t(buffer->getWidth()), uint16_t(buffer->getHeight()));
}
void DeviceContextBGFX::clearFramebuffer(unsigned int mask, const float color[4], float depth, unsigned int stencil)
{
uint16_t clearFlags = 0;
if (mask & Buffer_Color)
{
clearFlags |= BGFX_CLEAR_COLOR;
}
if (mask & Buffer_Depth)
{
clearFlags |= BGFX_CLEAR_DEPTH;
}
if (mask & Buffer_Stencil)
{
clearFlags |= BGFX_CLEAR_STENCIL;
}
uint32_t rgba = 0;
if (mask & Buffer_Color)
{
rgba = (static_cast<uint32_t>(color[0] * 255.0f) << 24) | (static_cast<uint32_t>(color[1] * 255.0f) << 16) |
(static_cast<uint32_t>(color[2] * 255.0f) << 8) | (static_cast<uint32_t>(color[3] * 255.0f));
}
bgfx::setViewClear(currentView, clearFlags, rgba, depth, static_cast<uint8_t>(stencil));
// Touch the view to ensure clear happens
bgfx::touch(currentView);
}
void DeviceContextBGFX::copyFramebuffer(Framebuffer* buffer, Texture* texture, int xOffset, int yOffset)
{
AYAASSERT(texture->getType() == Texture::Type_2D);
AYAASSERT(buffer->getWidth() == texture->getWidth() && buffer->getHeight() == texture->getHeight());
if (!device->getCapsBGFX().supportsTextureBlits)
{
// Fall back to a different method or log warning
return;
}
FramebufferBGFX* framebufferBGFX = static_cast<FramebufferBGFX*>(buffer);
TextureBGFX* textureBGFX = static_cast<TextureBGFX*>(texture);
// BGFX blit from framebuffer to texture
// Note: BGFX handles this differently - would need access to the framebuffer's texture attachments
invalidateCachedTextureStage(0);
// bgfx::blit(currentView, textureBGFX->getHandle(), 0, xOffset, yOffset, framebufferTexture, 0, 0, 0, width, height);
}
void DeviceContextBGFX::resolveFramebuffer(Framebuffer* msaaBuffer, Framebuffer* buffer, unsigned int mask)
{
AYAASSERT(msaaBuffer->getSamples() > 1);
AYAASSERT(buffer->getSamples() == 1);
AYAASSERT(msaaBuffer->getWidth() == buffer->getWidth() && msaaBuffer->getHeight() == buffer->getHeight());
// BGFX handles MSAA resolve automatically when rendering to a MSAA framebuffer
// and then using it as a texture or blitting to a non-MSAA target
// This may not need explicit implementation
}
void DeviceContextBGFX::discardFramebuffer(Framebuffer* buffer, unsigned int mask)
{
// BGFX handles framebuffer discard internally
// bgfx::discard() is used differently - it discards all pending draw calls
uint8_t discardFlags = 0;
// BGFX doesn't have per-buffer discard flags like GL
// The discard() function discards all state changes
if (mask != 0)
{
bgfx::discard();
}
}
void DeviceContextBGFX::bindProgram(ShaderProgram* program)
{
ShaderProgramBGFX* programBGFX = static_cast<ShaderProgramBGFX*>(program);
cachedProgram = programBGFX;
// BGFX sets the program per-draw call via bgfx::submit, not here
// Just cache the program for later use
}
void DeviceContextBGFX::setWorldTransforms4x3(const float* data, size_t matrixCount)
{
if (cachedProgram)
{
cachedProgram->setWorldTransforms4x3(data, matrixCount);
}
}
void DeviceContextBGFX::setConstant(int handle, const float* data, size_t vectorCount)
{
if (cachedProgram)
{
cachedProgram->setConstant(handle, data, vectorCount);
}
}
void DeviceContextBGFX::bindTexture(unsigned int stage, Texture* texture, const SamplerState& state)
{
SamplerState realState = (state.getFilter() == SamplerState::Filter_Anisotropic && state.getAnisotropy() == 0)
? SamplerState(state.getFilter(), state.getAddress(), defaultAnisotropy)
: state;
AYAASSERT(stage < device->getCaps().maxTextureUnits);
AYAASSERT(stage < ARRAYSIZE(cachedTextures));
TextureBGFX* textureBGFX = static_cast<TextureBGFX*>(texture);
if (textureBGFX != cachedTextures[stage])
{
cachedTextures[stage] = textureBGFX;
}
// Set texture with proper sampler uniform
textureBGFX->bindWithUniform(stage, samplerUniforms[stage], realState);
}
void DeviceContextBGFX::setRasterizerState(const RasterizerState& state)
{
if (cachedRasterizerState != state)
{
cachedRasterizerState = state;
// Update cached state flags
cachedStateFlags &= ~(BGFX_STATE_CULL_MASK);
cachedStateFlags |= getCullStateFlags(state.getCullMode());
// Handle depth bias (polygon offset)
if (state.getDepthBias() != 0)
{
// BGFX doesn't have a direct polygon offset equivalent in state flags
// This would need to be handled differently, possibly in shader or via uniforms
}
}
}
void DeviceContextBGFX::setBlendState(const BlendState& state)
{
if (cachedBlendState != state)
{
cachedBlendState = state;
// Update cached state flags
cachedStateFlags &= ~(BGFX_STATE_BLEND_MASK | BGFX_STATE_WRITE_MASK);
cachedStateFlags |= getBlendStateFlags(state);
cachedStateFlags |= getColorMaskFlags(state.getColorMask());
}
}
void DeviceContextBGFX::setDepthState(const DepthState& state)
{
if (cachedDepthState != state)
{
cachedDepthState = state;
// Update cached state flags
cachedStateFlags &= ~(BGFX_STATE_DEPTH_TEST_MASK | BGFX_STATE_WRITE_Z);
cachedStateFlags |= getDepthStateFlags(state);
// Handle stencil modes - BGFX uses bgfx::setStencil() separately
// For now, we'll handle basic stencil modes
// More complex stencil operations would need to be set per-draw call
switch (state.getStencilMode())
{
case DepthState::Stencil_None:
// No stencil test
break;
case DepthState::Stencil_IsNotZero:
case DepthState::Stencil_UpdateZFail:
case DepthState::Stencil_Increment:
case DepthState::Stencil_Decrement:
case DepthState::Stencil_IsNotZeroReplace:
// Stencil operations in BGFX require setting stencil per-draw
// Mark that stencil is needed
break;
default:
AYAASSERT(false);
}
}
}
void DeviceContextBGFX::handleStencilState()
{
// Convert DepthState stencil modes to BGFX stencil operations
uint32_t frontStencil = BGFX_STENCIL_NONE;
uint32_t backStencil = BGFX_STENCIL_NONE;
switch (cachedDepthState.getStencilMode())
{
case DepthState::Stencil_None:
// No stencil test
break;
case DepthState::Stencil_IsNotZero:
frontStencil = BGFX_STENCIL_TEST_NOTEQUAL | BGFX_STENCIL_FUNC_REF(0) | BGFX_STENCIL_FUNC_RMASK(0xFF) | BGFX_STENCIL_OP_FAIL_S_KEEP |
BGFX_STENCIL_OP_FAIL_Z_KEEP | BGFX_STENCIL_OP_PASS_Z_KEEP;
backStencil = frontStencil;
break;
case DepthState::Stencil_UpdateZFail:
// Front faces: decrement on z-fail
frontStencil = BGFX_STENCIL_TEST_ALWAYS | BGFX_STENCIL_FUNC_REF(0) | BGFX_STENCIL_FUNC_RMASK(0xFF) | BGFX_STENCIL_OP_FAIL_S_KEEP |
BGFX_STENCIL_OP_FAIL_Z_DECR | BGFX_STENCIL_OP_PASS_Z_KEEP;
// Back faces: increment on z-fail
backStencil = BGFX_STENCIL_TEST_ALWAYS | BGFX_STENCIL_FUNC_REF(0) | BGFX_STENCIL_FUNC_RMASK(0xFF) | BGFX_STENCIL_OP_FAIL_S_KEEP |
BGFX_STENCIL_OP_FAIL_Z_INCR | BGFX_STENCIL_OP_PASS_Z_KEEP;
break;
case DepthState::Stencil_Increment:
// This mode has special requirements - it modifies the state flags
// Disable color writes, enable front-face culling, increment stencil
frontStencil = BGFX_STENCIL_TEST_ALWAYS | BGFX_STENCIL_FUNC_REF(0) | BGFX_STENCIL_FUNC_RMASK(0xFF) | BGFX_STENCIL_OP_FAIL_S_KEEP |
BGFX_STENCIL_OP_FAIL_Z_KEEP | BGFX_STENCIL_OP_PASS_Z_INCR;
backStencil = frontStencil;
// Modify state flags: disable color writes, enable culling of front faces
cachedStateFlags &= ~BGFX_STATE_WRITE_MASK;
cachedStateFlags &= ~BGFX_STATE_CULL_MASK;
cachedStateFlags |= BGFX_STATE_CULL_CCW; // Cull front faces
break;
case DepthState::Stencil_Decrement:
// Continue from Increment - now cull back faces and decrement
frontStencil = BGFX_STENCIL_TEST_ALWAYS | BGFX_STENCIL_FUNC_REF(0) | BGFX_STENCIL_FUNC_RMASK(0xFF) | BGFX_STENCIL_OP_FAIL_S_KEEP |
BGFX_STENCIL_OP_FAIL_Z_KEEP | BGFX_STENCIL_OP_PASS_Z_DECR;
backStencil = frontStencil;
// Modify state flags: disable color writes, enable culling of back faces
cachedStateFlags &= ~BGFX_STATE_WRITE_MASK;
cachedStateFlags &= ~BGFX_STATE_CULL_MASK;
cachedStateFlags |= BGFX_STATE_CULL_CW; // Cull back faces
break;
case DepthState::Stencil_IsNotZeroReplace:
// Test stencil < 1, enable blending and color writes
frontStencil = BGFX_STENCIL_TEST_LESS | BGFX_STENCIL_FUNC_REF(1) | BGFX_STENCIL_FUNC_RMASK(0xFF) | BGFX_STENCIL_OP_FAIL_S_KEEP |
BGFX_STENCIL_OP_FAIL_Z_KEEP | BGFX_STENCIL_OP_PASS_Z_KEEP;
backStencil = frontStencil;
// Re-enable color writes, disable culling, enable blending
cachedStateFlags |= BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A;
cachedStateFlags &= ~BGFX_STATE_CULL_MASK;
cachedStateFlags |= BGFX_STATE_CULL_CW | BGFX_STATE_CULL_CCW; // Disable culling
// Enable alpha blending for shadow compositing
cachedStateFlags &= ~BGFX_STATE_BLEND_MASK;
cachedStateFlags |= BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_SRC_ALPHA, BGFX_STATE_BLEND_INV_SRC_ALPHA);
break;
default:
AYAASSERT(false);
}
if (frontStencil != BGFX_STENCIL_NONE || backStencil != BGFX_STENCIL_NONE)
{
bgfx::setStencil(frontStencil, backStencil);
}
}
void DeviceContextBGFX::drawImpl(Geometry* geometry, Geometry::Primitive primitive, unsigned int offset, unsigned int count,
unsigned int indexRangeBegin, unsigned int indexRangeEnd)
{
AYAASSERT(cachedProgram != nullptr);
// Update global constants if needed
if (cachedProgram)
{
cachedProgram->updateGlobalConstants(&globalData[0], globalDataVersion);
}
// Set the accumulated state flags
uint64_t stateFlags = cachedStateFlags;
// Ensure we always write to RGB and A unless masked
if ((stateFlags & BGFX_STATE_WRITE_MASK) == 0)
{
stateFlags |= BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A;
}
bgfx::setState(stateFlags);
// Handle stencil state separately
handleStencilState();
// Set vertex and index buffers - GeometryBGFX handles binding
GeometryBGFX* geometryBGFX = static_cast<GeometryBGFX*>(geometry);
geometryBGFX->bindBuffers(offset, count);
// CRITICAL: Actually submit the draw call with the shader program
// This is what actually triggers rendering in BGFX!
bgfx::submit(currentView, cachedProgram->getHandle());
}
void DeviceContextBGFX::pushDebugMarkerGroup(const char* text)
{
bgfx::setViewName(currentView, text);
}
void DeviceContextBGFX::popDebugMarkerGroup()
{
// BGFX doesn't have a direct equivalent to pop marker groups
// Markers are set per-view
}
void DeviceContextBGFX::setDebugMarker(const char* text)
{
bgfx::setMarker(text);
}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,148 @@
#include "FramebufferBGFX.hpp"
#include "TextureBGFX.hpp"
#include "DeviceBGFX.hpp"
#include <bgfx/bgfx.h>
namespace Aya
{
namespace Graphics
{
RenderbufferBGFX::RenderbufferBGFX(Device* device, Texture::Format format, unsigned int width, unsigned int height, unsigned int samples)
: Renderbuffer(device, format, width, height, samples)
{
AYAASSERT(samples);
if (samples > device->getCaps().maxSamples)
{
throw Aya::runtime_error("Unsupported renderbuffer: too many samples (%d)", samples);
}
// Create a texture that will be used as a renderbuffer
bgfx::TextureFormat::Enum bgfxFormat = TextureBGFX::getBGFXFormat(format);
if (bgfxFormat == bgfx::TextureFormat::Unknown)
{
throw Aya::runtime_error("Unsupported renderbuffer format");
}
uint64_t flags = BGFX_TEXTURE_RT;
if (samples > 1)
{
flags |= BGFX_TEXTURE_RT_MSAA_X4; // BGFX has predefined MSAA levels
}
textureHandle = bgfx::createTexture2D(width, height, false, 1, bgfxFormat, flags);
if (!bgfx::isValid(textureHandle))
{
throw Aya::runtime_error("Failed to create renderbuffer texture");
}
}
RenderbufferBGFX::~RenderbufferBGFX()
{
if (bgfx::isValid(textureHandle))
{
bgfx::destroy(textureHandle);
}
}
FramebufferBGFX::FramebufferBGFX(Device* device, bgfx::FrameBufferHandle handle)
: Framebuffer(device, 0, 0, 1)
, handle(handle)
, isMain(true)
{
// Main framebuffer - dimensions will be set by the window
}
FramebufferBGFX::FramebufferBGFX(Device* device, const std::vector<shared_ptr<Renderbuffer>>& color, const shared_ptr<Renderbuffer>& depth)
: Framebuffer(device, 0, 0, 0)
, handle(BGFX_INVALID_HANDLE)
, isMain(false)
, color(color)
, depth(depth)
{
AYAASSERT(!color.empty());
if (color.size() > device->getCaps().maxDrawBuffers)
{
throw Aya::runtime_error("Unsupported framebuffer configuration: too many buffers (%d)", (int)color.size());
}
// Collect all texture handles for the framebuffer
std::vector<bgfx::TextureHandle> textures;
for (size_t i = 0; i < color.size(); ++i)
{
RenderbufferBGFX* buffer = static_cast<RenderbufferBGFX*>(color[i].get());
AYAASSERT(buffer);
AYAASSERT(!Texture::isFormatDepth(buffer->getFormat()));
textures.push_back(buffer->getTextureHandle());
if (i == 0)
{
width = buffer->getWidth();
height = buffer->getHeight();
samples = buffer->getSamples();
}
else
{
AYAASSERT(width == buffer->getWidth());
AYAASSERT(height == buffer->getHeight());
AYAASSERT(samples == buffer->getSamples());
}
}
// Add depth buffer if present
if (depth)
{
RenderbufferBGFX* buffer = static_cast<RenderbufferBGFX*>(depth.get());
AYAASSERT(Texture::isFormatDepth(buffer->getFormat()));
AYAASSERT(width == buffer->getWidth());
AYAASSERT(height == buffer->getHeight());
AYAASSERT(samples == buffer->getSamples());
textures.push_back(buffer->getTextureHandle());
}
// Create framebuffer from textures
handle = bgfx::createFrameBuffer(textures.size(), textures.data(), false);
if (!bgfx::isValid(handle))
{
throw Aya::runtime_error("Failed to create framebuffer");
}
}
FramebufferBGFX::~FramebufferBGFX()
{
if (!isMain && bgfx::isValid(handle))
{
bgfx::destroy(handle);
}
}
void FramebufferBGFX::download(void* data, unsigned int size)
{
AYAASSERT(size == width * height * 4);
// BGFX doesn't support synchronous framebuffer downloads like OpenGL
// You would need to use bgfx::readTexture which is asynchronous
// and requires a callback
// For now, we'll just assert that this isn't implemented
// In a real implementation, you'd need to:
// 1. Create a callback to receive the data
// 2. Call bgfx::readTexture on the color attachment
// 3. Wait for the callback (which might require frame coordination)
AYAASSERT(false && "Framebuffer download not implemented for BGFX");
}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,53 @@
#pragma once
#include "Core/Framebuffer.hpp"
#include <bgfx/bgfx.h>
#include <vector>
namespace Aya
{
namespace Graphics
{
class DeviceBGFX;
class TextureBGFX;
class RenderbufferBGFX : public Renderbuffer
{
public:
RenderbufferBGFX(Device* device, Texture::Format format, unsigned int width, unsigned int height, unsigned int samples);
~RenderbufferBGFX();
bgfx::TextureHandle getTextureHandle() const
{
return textureHandle;
}
private:
bgfx::TextureHandle textureHandle;
};
class FramebufferBGFX : public Framebuffer
{
public:
FramebufferBGFX(Device* device, bgfx::FrameBufferHandle handle);
FramebufferBGFX(Device* device, const std::vector<shared_ptr<Renderbuffer>>& color, const shared_ptr<Renderbuffer>& depth);
~FramebufferBGFX();
virtual void download(void* data, unsigned int size);
bgfx::FrameBufferHandle getHandle() const
{
return handle;
}
private:
bgfx::FrameBufferHandle handle;
bool isMain;
std::vector<shared_ptr<Renderbuffer>> color;
shared_ptr<Renderbuffer> depth;
};
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,343 @@
#include "GeometryBGFX.hpp"
#include "DeviceBGFX.hpp"
#include <bgfx/bgfx.h>
namespace Aya
{
namespace Graphics
{
bgfx::Attrib::Enum VertexLayoutBGFX::getAttribType(unsigned int semanticId)
{
switch (semanticId)
{
case 0: // Position
return bgfx::Attrib::Position;
case 1: // Normal
return bgfx::Attrib::Normal;
case 2: // Color0
return bgfx::Attrib::Color0;
case 3: // Color1
return bgfx::Attrib::Color1;
case 4: // TexCoord0
return bgfx::Attrib::TexCoord0;
case 5: // TexCoord1
return bgfx::Attrib::TexCoord1;
case 6: // TexCoord2
return bgfx::Attrib::TexCoord2;
case 7: // TexCoord3
return bgfx::Attrib::TexCoord3;
case 8: // TexCoord4
return bgfx::Attrib::TexCoord4;
case 9: // TexCoord5
return bgfx::Attrib::TexCoord5;
case 10: // TexCoord6
return bgfx::Attrib::TexCoord6;
case 11: // TexCoord7
return bgfx::Attrib::TexCoord7;
default:
return bgfx::Attrib::Count;
}
}
static bgfx::AttribType::Enum getAttribFormat(VertexLayout::Format format)
{
switch (format)
{
case VertexLayout::Format_Float1:
case VertexLayout::Format_Float2:
case VertexLayout::Format_Float3:
case VertexLayout::Format_Float4:
return bgfx::AttribType::Float;
case VertexLayout::Format_Short2:
case VertexLayout::Format_Short4:
return bgfx::AttribType::Int16;
case VertexLayout::Format_UByte4:
case VertexLayout::Format_Color:
return bgfx::AttribType::Uint8;
default:
return bgfx::AttribType::Count;
}
}
static uint8_t getAttribCount(VertexLayout::Format format)
{
switch (format)
{
case VertexLayout::Format_Float1:
return 1;
case VertexLayout::Format_Float2:
case VertexLayout::Format_Short2:
return 2;
case VertexLayout::Format_Float3:
return 3;
case VertexLayout::Format_Float4:
case VertexLayout::Format_Short4:
case VertexLayout::Format_UByte4:
case VertexLayout::Format_Color:
return 4;
default:
return 0;
}
}
static bool isNormalized(VertexLayout::Format format)
{
return format == VertexLayout::Format_Color;
}
static unsigned int getVertexAttributeId(VertexLayout::Semantic semantic, unsigned int index)
{
switch (semantic)
{
case VertexLayout::Semantic_Position:
AYAASSERT(index == 0);
return 0;
case VertexLayout::Semantic_Normal:
AYAASSERT(index == 0);
return 1;
case VertexLayout::Semantic_Color:
AYAASSERT(index < 2);
return 2 + index;
case VertexLayout::Semantic_Texture:
AYAASSERT(index < 8);
return 4 + index;
default:
AYAASSERT(false);
return 0;
}
}
VertexLayoutBGFX::VertexLayoutBGFX(Device* device, const std::vector<Element>& elements)
: VertexLayout(device, elements)
{
layout.begin();
for (size_t i = 0; i < elements.size(); ++i)
{
const Element& e = elements[i];
unsigned int attribId = getVertexAttributeId(e.semantic, e.semanticIndex);
bgfx::Attrib::Enum attrib = getAttribType(attribId);
bgfx::AttribType::Enum type = getAttribFormat(e.format);
uint8_t count = getAttribCount(e.format);
bool normalized = isNormalized(e.format);
layout.add(attrib, count, type, normalized);
}
layout.end();
}
VertexLayoutBGFX::~VertexLayoutBGFX() {}
template<typename Base>
GeometryBufferBGFX<Base>::GeometryBufferBGFX(Device* device, size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage, bool isIndex)
: Base(device, elementSize, elementCount, usage)
, isIndex(isIndex)
, memory(nullptr)
, locked(nullptr)
{
vertexHandle = BGFX_INVALID_HANDLE;
indexHandle = BGFX_INVALID_HANDLE;
}
template<typename Base>
void GeometryBufferBGFX<Base>::create()
{
uint32_t size = this->elementSize * this->elementCount;
if (isIndex)
{
uint16_t flags = (this->usage == GeometryBuffer::Usage_Dynamic) ? BGFX_BUFFER_NONE : BGFX_BUFFER_NONE;
indexHandle = bgfx::createDynamicIndexBuffer(this->elementCount, flags);
}
else
{
// Vertex buffers need a layout - we'll create the handle later in GeometryBGFX
// when we have access to the actual layout
vertexHandle = BGFX_INVALID_HANDLE;
}
}
template<typename Base>
void GeometryBufferBGFX<Base>::createVertexBuffer(const bgfx::VertexLayout& layout)
{
AYAASSERT(!isIndex);
AYAASSERT(!bgfx::isValid(vertexHandle));
uint16_t flags = (this->usage == GeometryBuffer::Usage_Dynamic) ? BGFX_BUFFER_NONE : BGFX_BUFFER_NONE;
vertexHandle = bgfx::createDynamicVertexBuffer(this->elementCount, layout, flags);
}
template<typename Base>
GeometryBufferBGFX<Base>::~GeometryBufferBGFX()
{
AYAASSERT(!locked);
if (bgfx::isValid(vertexHandle))
{
bgfx::destroy(vertexHandle);
}
if (bgfx::isValid(indexHandle))
{
bgfx::destroy(indexHandle);
}
}
template<typename Base>
void* GeometryBufferBGFX<Base>::lock(GeometryBuffer::LockMode mode)
{
AYAASSERT(!locked);
unsigned int size = this->elementSize * this->elementCount;
// Allocate temporary buffer
locked = new char[size];
AYAASSERT(locked);
return locked;
}
template<typename Base>
void GeometryBufferBGFX<Base>::unlock()
{
AYAASSERT(locked);
// Upload data to BGFX
upload(0, locked, this->elementSize * this->elementCount);
delete[] static_cast<char*>(locked);
locked = nullptr;
}
template<typename Base>
void GeometryBufferBGFX<Base>::upload(unsigned int offset, const void* data, unsigned int size)
{
AYAASSERT(!locked);
AYAASSERT(offset + size <= this->elementSize * this->elementCount);
const bgfx::Memory* mem = bgfx::copy(static_cast<const char*>(data) + offset, size);
if (isIndex)
{
if (bgfx::isValid(indexHandle))
{
bgfx::update(indexHandle, 0, mem);
}
}
else
{
if (bgfx::isValid(vertexHandle))
{
bgfx::update(vertexHandle, 0, mem);
}
}
}
VertexBufferBGFX::VertexBufferBGFX(Device* device, size_t elementSize, size_t elementCount, Usage usage)
: GeometryBufferBGFX<VertexBuffer>(device, elementSize, elementCount, usage, false)
{
create();
}
VertexBufferBGFX::~VertexBufferBGFX() {}
IndexBufferBGFX::IndexBufferBGFX(Device* device, size_t elementSize, size_t elementCount, Usage usage)
: GeometryBufferBGFX<IndexBuffer>(device, elementSize, elementCount, usage, true)
{
if (elementSize != 2 && elementSize != 4)
throw Aya::runtime_error("Invalid element size: %d", (int)elementSize);
create();
}
IndexBufferBGFX::~IndexBufferBGFX() {}
GeometryBGFX::GeometryBGFX(Device* device, const shared_ptr<VertexLayout>& layout, const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers,
const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex)
: Geometry(device, layout, vertexBuffers, indexBuffer, baseVertexIndex)
, indexElementSize(0)
{
if (indexBuffer)
{
indexElementSize = indexBuffer->getElementSize();
}
// Create vertex buffers with proper layout
VertexLayoutBGFX* layoutBGFX = static_cast<VertexLayoutBGFX*>(layout.get());
const bgfx::VertexLayout& bgfxLayout = layoutBGFX->getBGFXLayout();
for (size_t i = 0; i < vertexBuffers.size(); ++i)
{
VertexBufferBGFX* vb = static_cast<VertexBufferBGFX*>(vertexBuffers[i].get());
// Create the vertex buffer handle with the layout if not already created
if (!bgfx::isValid(vb->getVertexHandle()))
{
vb->createVertexBuffer(bgfxLayout);
}
}
}
GeometryBGFX::~GeometryBGFX() {}
void GeometryBGFX::bindBuffers(unsigned int offset, unsigned int count)
{
// Set vertex buffers
for (size_t i = 0; i < vertexBuffers.size(); ++i)
{
VertexBufferBGFX* vb = static_cast<VertexBufferBGFX*>(vertexBuffers[i].get());
if (bgfx::isValid(vb->getVertexHandle()))
{
bgfx::setVertexBuffer(i, vb->getVertexHandle(), baseVertexIndex, vb->getElementCount());
}
}
// Set index buffer if present
if (indexBuffer)
{
IndexBufferBGFX* ib = static_cast<IndexBufferBGFX*>(indexBuffer.get());
if (bgfx::isValid(ib->getIndexHandle()))
{
bgfx::setIndexBuffer(ib->getIndexHandle(), offset, count);
}
}
}
uint64_t GeometryBGFX::convertPrimitive(Primitive primitive)
{
// This function is not used in BGFX - primitives are set via state flags
// Keeping for potential future use
switch (primitive)
{
case Primitive_Triangles:
return BGFX_STATE_PT_TRISTRIP;
case Primitive_Lines:
return BGFX_STATE_PT_LINES;
case Primitive_Points:
return BGFX_STATE_PT_POINTS;
case Primitive_TriangleStrip:
return BGFX_STATE_PT_TRISTRIP;
default:
return BGFX_STATE_PT_TRISTRIP;
}
}
void GeometryBGFX::draw(bgfx::ViewId view, Primitive primitive, unsigned int offset, unsigned int count)
{
// Legacy method - not used anymore
// Drawing is now handled by bindBuffers() + bgfx::submit() in DeviceContextBGFX::drawImpl()
}
// Explicit template instantiation
template class GeometryBufferBGFX<VertexBuffer>;
template class GeometryBufferBGFX<IndexBuffer>;
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,100 @@
#pragma once
#include "Core/Geometry.hpp"
#include <bgfx/bgfx.h>
namespace Aya
{
namespace Graphics
{
class DeviceBGFX;
class VertexLayoutBGFX : public VertexLayout
{
public:
VertexLayoutBGFX(Device* device, const std::vector<Element>& elements);
~VertexLayoutBGFX();
const bgfx::VertexLayout& getBGFXLayout() const
{
return layout;
}
static bgfx::Attrib::Enum getAttribType(unsigned int semanticId);
private:
bgfx::VertexLayout layout;
};
template<typename Base>
class GeometryBufferBGFX : public Base
{
public:
GeometryBufferBGFX(Device* device, size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage, bool isIndex);
~GeometryBufferBGFX();
virtual void* lock(GeometryBuffer::LockMode mode);
virtual void unlock();
virtual void upload(unsigned int offset, const void* data, unsigned int size);
void createVertexBuffer(const bgfx::VertexLayout& layout);
bgfx::DynamicVertexBufferHandle getVertexHandle() const
{
return vertexHandle;
}
bgfx::DynamicIndexBufferHandle getIndexHandle() const
{
return indexHandle;
}
const bgfx::Memory* getMemory() const
{
return memory;
}
protected:
void create();
private:
bool isIndex;
bgfx::DynamicVertexBufferHandle vertexHandle;
bgfx::DynamicIndexBufferHandle indexHandle;
const bgfx::Memory* memory;
void* locked;
};
class VertexBufferBGFX : public GeometryBufferBGFX<VertexBuffer>
{
public:
VertexBufferBGFX(Device* device, size_t elementSize, size_t elementCount, Usage usage);
~VertexBufferBGFX();
};
class IndexBufferBGFX : public GeometryBufferBGFX<IndexBuffer>
{
public:
IndexBufferBGFX(Device* device, size_t elementSize, size_t elementCount, Usage usage);
~IndexBufferBGFX();
};
class GeometryBGFX : public Geometry
{
public:
GeometryBGFX(Device* device, const shared_ptr<VertexLayout>& layout, const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers,
const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex);
~GeometryBGFX();
void bindBuffers(unsigned int offset, unsigned int count);
void draw(bgfx::ViewId view, Primitive primitive, unsigned int offset, unsigned int count);
private:
unsigned int indexElementSize;
static uint64_t convertPrimitive(Primitive primitive);
};
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,14 @@
#pragma once
// BGFX headers
#include <bgfx/bgfx.h>
#include <bgfx/platform.h>
// Common macros
#ifndef AYAASSERT
#define AYAASSERT(x) assert(x)
#endif
#ifndef ARRAYSIZE
#define ARRAYSIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#endif

View File

@@ -0,0 +1,293 @@
#include "ShaderBGFX.hpp"
#include "DeviceBGFX.hpp"
#include "GeometryBGFX.hpp"
#include <bgfx/bgfx.h>
LOGGROUP(Graphics)
namespace Aya
{
namespace Graphics
{
VertexShaderBGFX::VertexShaderBGFX(Device* device, const std::vector<char>& bytecode)
: VertexShader(device)
{
const bgfx::Memory* mem = bgfx::copy(bytecode.data(), bytecode.size());
handle = bgfx::createShader(mem);
if (!bgfx::isValid(handle))
{
throw std::runtime_error("Failed to create vertex shader");
}
}
VertexShaderBGFX::~VertexShaderBGFX()
{
if (bgfx::isValid(handle))
{
bgfx::destroy(handle);
}
}
void VertexShaderBGFX::reloadBytecode(const std::vector<char>& bytecode)
{
if (bgfx::isValid(handle))
{
bgfx::destroy(handle);
}
const bgfx::Memory* mem = bgfx::copy(bytecode.data(), bytecode.size());
handle = bgfx::createShader(mem);
if (!bgfx::isValid(handle))
{
throw std::runtime_error("Failed to reload vertex shader");
}
}
FragmentShaderBGFX::FragmentShaderBGFX(Device* device, const std::vector<char>& bytecode)
: FragmentShader(device)
{
const bgfx::Memory* mem = bgfx::copy(bytecode.data(), bytecode.size());
handle = bgfx::createShader(mem);
if (!bgfx::isValid(handle))
{
throw std::runtime_error("Failed to create fragment shader");
}
}
FragmentShaderBGFX::~FragmentShaderBGFX()
{
if (bgfx::isValid(handle))
{
bgfx::destroy(handle);
}
}
void FragmentShaderBGFX::reloadBytecode(const std::vector<char>& bytecode)
{
if (bgfx::isValid(handle))
{
bgfx::destroy(handle);
}
const bgfx::Memory* mem = bgfx::copy(bytecode.data(), bytecode.size());
handle = bgfx::createShader(mem);
if (!bgfx::isValid(handle))
{
throw std::runtime_error("Failed to reload fragment shader");
}
}
ShaderProgramBGFX::ShaderProgramBGFX(Device* device, const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader)
: ShaderProgram(device, vertexShader, fragmentShader)
, cachedGlobalVersion(0)
, maxWorldTransforms(0)
, samplerMask(0)
, nextConstantHandle(0)
{
VertexShaderBGFX* vs = static_cast<VertexShaderBGFX*>(vertexShader.get());
FragmentShaderBGFX* fs = static_cast<FragmentShaderBGFX*>(fragmentShader.get());
handle = bgfx::createProgram(vs->getHandle(), fs->getHandle(), false);
if (!bgfx::isValid(handle))
{
throw std::runtime_error("Failed to create shader program");
}
// Create uniforms for world matrices
worldMatrixUniform = bgfx::createUniform("u_worldMatrix", bgfx::UniformType::Mat4);
worldMatrixArrayUniform = bgfx::createUniform("u_worldMatrixArray", bgfx::UniformType::Vec4);
if (bgfx::isValid(worldMatrixArrayUniform))
{
maxWorldTransforms = 32; // Default maximum, can be adjusted
}
else if (bgfx::isValid(worldMatrixUniform))
{
maxWorldTransforms = 1;
}
// In BGFX, samplers are bound separately, so we'll assume all texture stages could be used
samplerMask = 0xFFFF;
}
ShaderProgramBGFX::~ShaderProgramBGFX()
{
if (bgfx::isValid(handle))
{
bgfx::destroy(handle);
}
if (bgfx::isValid(worldMatrixUniform))
{
bgfx::destroy(worldMatrixUniform);
}
if (bgfx::isValid(worldMatrixArrayUniform))
{
bgfx::destroy(worldMatrixArrayUniform);
}
// Destroy all created uniforms
for (auto& pair : uniforms)
{
if (bgfx::isValid(pair.second.handle))
{
bgfx::destroy(pair.second.handle);
}
}
}
int ShaderProgramBGFX::getConstantHandle(const char* name) const
{
// Check if we already have this uniform
for (const auto& pair : handleToName)
{
if (pair.second == name)
{
return pair.first;
}
}
// Create new handle - we need to make this non-const to modify handleToName
// In practice, this is fine as we're just caching uniform lookups
ShaderProgramBGFX* mutableThis = const_cast<ShaderProgramBGFX*>(this);
// Create uniform on first use
std::string uniformName = name;
bgfx::UniformHandle uniformHandle = bgfx::createUniform(uniformName.c_str(), bgfx::UniformType::Vec4);
if (bgfx::isValid(uniformHandle))
{
int handle = mutableThis->nextConstantHandle++;
UniformInfo info;
info.handle = uniformHandle;
info.type = bgfx::UniformType::Vec4;
info.size = 1;
mutableThis->uniforms[uniformName] = info;
mutableThis->handleToName[handle] = uniformName;
return handle;
}
return -1;
}
unsigned int ShaderProgramBGFX::getMaxWorldTransforms() const
{
return maxWorldTransforms;
}
unsigned int ShaderProgramBGFX::getSamplerMask() const
{
return samplerMask;
}
void ShaderProgramBGFX::updateGlobalConstants(const void* globalData, unsigned int globalVersion)
{
if (cachedGlobalVersion == globalVersion)
return;
cachedGlobalVersion = globalVersion;
// Update global constants
const std::vector<ShaderGlobalConstant>& globalConstants = static_cast<DeviceBGFX*>(device)->getGlobalConstants();
for (size_t i = 0; i < globalConstants.size(); ++i)
{
const ShaderGlobalConstant& gc = globalConstants[i];
// Check if we have a uniform for this constant
auto it = uniforms.find(gc.name);
if (it == uniforms.end())
{
// Create uniform on first use
std::string uniformName = gc.name;
bgfx::UniformHandle uniformHandle = bgfx::createUniform(uniformName.c_str(), bgfx::UniformType::Vec4);
if (bgfx::isValid(uniformHandle))
{
UniformInfo info;
info.handle = uniformHandle;
info.type = bgfx::UniformType::Vec4;
info.size = 1;
uniforms[uniformName] = info;
it = uniforms.find(uniformName);
}
else
{
continue;
}
}
const float* data = reinterpret_cast<const float*>(static_cast<const char*>(globalData) + gc.offset);
bgfx::setUniform(it->second.handle, data);
}
}
void ShaderProgramBGFX::setWorldTransforms4x3(const float* data, size_t matrixCount)
{
if (matrixCount == 0)
return;
if (matrixCount == 1 && bgfx::isValid(worldMatrixUniform))
{
// Convert 4x3 matrix to 4x4
float matrix[16];
matrix[0] = data[0];
matrix[1] = data[1];
matrix[2] = data[2];
matrix[3] = 0.0f;
matrix[4] = data[4];
matrix[5] = data[5];
matrix[6] = data[6];
matrix[7] = 0.0f;
matrix[8] = data[8];
matrix[9] = data[9];
matrix[10] = data[10];
matrix[11] = 0.0f;
matrix[12] = data[12];
matrix[13] = data[13];
matrix[14] = data[14];
matrix[15] = 1.0f;
bgfx::setUniform(worldMatrixUniform, matrix);
}
else if (bgfx::isValid(worldMatrixArrayUniform))
{
AYAASSERT(matrixCount <= maxWorldTransforms);
// Pass array of vec4s (3 per matrix for 4x3)
bgfx::setUniform(worldMatrixArrayUniform, data, matrixCount * 3);
}
}
void ShaderProgramBGFX::setConstant(int handle, const float* data, size_t vectorCount)
{
if (handle < 0)
return;
auto it = handleToName.find(handle);
if (it == handleToName.end())
return;
auto uniformIt = uniforms.find(it->second);
if (uniformIt == uniforms.end())
return;
bgfx::setUniform(uniformIt->second.handle, data, vectorCount);
}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,93 @@
#pragma once
#include "Core/Shader.hpp"
#include <bgfx/bgfx.h>
#include <vector>
#include <map>
namespace Aya
{
namespace Graphics
{
class DeviceBGFX;
class VertexShaderBGFX : public VertexShader
{
public:
VertexShaderBGFX(Device* device, const std::vector<char>& bytecode);
~VertexShaderBGFX();
virtual void reloadBytecode(const std::vector<char>& bytecode);
bgfx::ShaderHandle getHandle() const
{
return handle;
}
private:
bgfx::ShaderHandle handle;
};
class FragmentShaderBGFX : public FragmentShader
{
public:
FragmentShaderBGFX(Device* device, const std::vector<char>& bytecode);
~FragmentShaderBGFX();
virtual void reloadBytecode(const std::vector<char>& bytecode);
bgfx::ShaderHandle getHandle() const
{
return handle;
}
private:
bgfx::ShaderHandle handle;
};
class ShaderProgramBGFX : public ShaderProgram
{
public:
ShaderProgramBGFX(Device* device, const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader);
~ShaderProgramBGFX();
virtual int getConstantHandle(const char* name) const;
virtual unsigned int getMaxWorldTransforms() const;
virtual unsigned int getSamplerMask() const;
void updateGlobalConstants(const void* globalData, unsigned int globalVersion);
void setWorldTransforms4x3(const float* data, size_t matrixCount);
void setConstant(int handle, const float* data, size_t vectorCount);
bgfx::ProgramHandle getHandle() const
{
return handle;
}
private:
bgfx::ProgramHandle handle;
struct UniformInfo
{
bgfx::UniformHandle handle;
bgfx::UniformType::Enum type;
unsigned int size;
};
std::map<std::string, UniformInfo> uniforms;
std::map<int, std::string> handleToName;
bgfx::UniformHandle worldMatrixUniform;
bgfx::UniformHandle worldMatrixArrayUniform;
unsigned int cachedGlobalVersion;
unsigned int maxWorldTransforms;
unsigned int samplerMask;
int nextConstantHandle;
};
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,269 @@
#include "TextureBGFX.hpp"
#include "DeviceBGFX.hpp"
#include "FramebufferBGFX.hpp"
#include <bgfx/bgfx.h>
LOGGROUP(Graphics)
namespace Aya
{
namespace Graphics
{
bgfx::TextureFormat::Enum TextureBGFX::getBGFXFormat(Format format)
{
switch (format)
{
case Format_L8:
return bgfx::TextureFormat::R8;
case Format_LA8:
return bgfx::TextureFormat::RG8;
case Format_RGB5A1:
return bgfx::TextureFormat::RGB5A1;
case Format_RGBA8:
return bgfx::TextureFormat::RGBA8;
case Format_RG16:
return bgfx::TextureFormat::RG16;
case Format_RGBA16F:
return bgfx::TextureFormat::RGBA16F;
case Format_BC1:
return bgfx::TextureFormat::BC1;
case Format_BC2:
return bgfx::TextureFormat::BC2;
case Format_BC3:
return bgfx::TextureFormat::BC3;
case Format_PVRTC_RGB2:
return bgfx::TextureFormat::PTC12;
case Format_PVRTC_RGBA2:
return bgfx::TextureFormat::PTC12A;
case Format_PVRTC_RGB4:
return bgfx::TextureFormat::PTC14;
case Format_PVRTC_RGBA4:
return bgfx::TextureFormat::PTC14A;
case Format_ETC1:
return bgfx::TextureFormat::ETC1;
case Format_D16:
return bgfx::TextureFormat::D16;
case Format_D24S8:
return bgfx::TextureFormat::D24S8;
default:
return bgfx::TextureFormat::Unknown;
}
}
static uint64_t getBGFXTextureFlags(Texture::Usage usage, unsigned int mipLevels)
{
uint64_t flags = BGFX_TEXTURE_NONE;
if (usage == Texture::Usage_Renderbuffer)
{
flags |= BGFX_TEXTURE_RT;
}
if (mipLevels > 1)
{
flags |= BGFX_TEXTURE_NONE; // Mips are created by default
}
return flags;
}
static uint32_t getBGFXSamplerFlags(const SamplerState& state)
{
uint32_t flags = BGFX_SAMPLER_NONE;
// Filter mode
switch (state.getFilter())
{
case SamplerState::Filter_Point:
flags |= BGFX_SAMPLER_MIN_POINT | BGFX_SAMPLER_MAG_POINT | BGFX_SAMPLER_MIP_POINT;
break;
case SamplerState::Filter_Linear:
flags |= BGFX_SAMPLER_MIN_ANISOTROPIC | BGFX_SAMPLER_MAG_ANISOTROPIC;
break;
case SamplerState::Filter_Anisotropic:
flags |= BGFX_SAMPLER_MIN_ANISOTROPIC | BGFX_SAMPLER_MAG_ANISOTROPIC;
break;
}
// Address mode
switch (state.getAddress())
{
case SamplerState::Address_Wrap:
// Wrap is the default - no flags needed
break;
case SamplerState::Address_Clamp:
flags |= BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP | BGFX_SAMPLER_W_CLAMP;
break;
}
return flags;
}
TextureBGFX::TextureBGFX(
Device* device, Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, Usage usage)
: Texture(device, type, format, width, height, depth, mipLevels, usage)
, cachedState(SamplerState::Filter_Count)
{
bgfx::TextureFormat::Enum bgfxFormat = getBGFXFormat(format);
if (bgfxFormat == bgfx::TextureFormat::Unknown)
{
throw Aya::runtime_error("Unsupported texture format");
}
uint64_t flags = getBGFXTextureFlags(usage, mipLevels);
if (type == Type_3D)
{
handle = bgfx::createTexture3D(width, height, depth, mipLevels > 1, bgfxFormat, flags);
}
else if (type == Type_Cube)
{
handle = bgfx::createTextureCube(width, mipLevels > 1, 1, bgfxFormat, flags);
}
else if (type == Type_2DMultisampled)
{
handle = bgfx::createTexture2D(width, height, mipLevels > 1, 1, bgfxFormat, flags | BGFX_TEXTURE_RT_MSAA_X4);
}
else // Type_2D
{
handle = bgfx::createTexture2D(width, height, mipLevels > 1, 1, bgfxFormat, flags);
}
if (!bgfx::isValid(handle))
{
throw Aya::runtime_error("Failed to create texture");
}
}
TextureBGFX::~TextureBGFX()
{
if (bgfx::isValid(handle))
{
bgfx::destroy(handle);
}
}
void TextureBGFX::upload(unsigned int index, unsigned int mip, const TextureRegion& region, const void* data, unsigned int size)
{
AYAASSERT(index < (type == Type_Cube ? 6u : 1u));
AYAASSERT(mip < mipLevels);
unsigned int mipWidth = getMipSide(width, mip);
unsigned int mipHeight = getMipSide(height, mip);
unsigned int mipDepth = getMipSide(depth, mip);
AYAASSERT(region.x + region.width <= mipWidth);
AYAASSERT(region.y + region.height <= mipHeight);
AYAASSERT(region.z + region.depth <= mipDepth);
AYAASSERT(size == getImageSize(format, region.width, region.height) * region.depth);
const bgfx::Memory* mem = bgfx::copy(data, size);
if (type == Type_3D)
{
bgfx::updateTexture3D(handle, mip, region.x, region.y, region.z, region.width, region.height, region.depth, mem);
}
else if (type == Type_Cube)
{
bgfx::updateTextureCube(handle, 0, index, mip, region.x, region.y, region.width, region.height, mem);
}
else
{
bgfx::updateTexture2D(handle, 0, mip, region.x, region.y, region.width, region.height, mem);
}
}
bool TextureBGFX::download(unsigned int index, unsigned int mip, void* data, unsigned int size)
{
// BGFX doesn't support direct texture download in the same way as GL
// This would require reading from a framebuffer that the texture is attached to
const DeviceCapsBGFX& caps = static_cast<DeviceBGFX*>(device)->getCapsBGFX();
if (!caps.supportsTextureReadBack)
{
return false;
}
// Reading back texture data would require creating a framebuffer and using bgfx::readTexture
// which is async in BGFX
return false;
}
bool TextureBGFX::supportsLocking() const
{
// BGFX doesn't support texture locking in the traditional sense
return false;
}
Texture::LockResult TextureBGFX::lock(unsigned int index, unsigned int mip, const TextureRegion& region)
{
// BGFX doesn't support direct texture locking
LockResult result = {0, 0, 0};
return result;
}
void TextureBGFX::unlock(unsigned int index, unsigned int mip)
{
// BGFX doesn't support direct texture locking
}
shared_ptr<Renderbuffer> TextureBGFX::getRenderbuffer(unsigned int index, unsigned int mip)
{
AYAASSERT(index < (type == Type_Cube ? 6u : 1u));
AYAASSERT(mip < mipLevels);
AYAASSERT(usage == Usage_Renderbuffer);
std::pair<unsigned, unsigned> key(index, mip);
shared_ptr<Renderbuffer> result = renderBuffers[key].lock();
if (!result)
{
result.reset(new RenderbufferBGFX(device, format, getMipSide(width, mip), getMipSide(height, mip), getType() == Type_2DMultisampled ? 4 : 1));
renderBuffers[key] = result;
}
return result;
}
void TextureBGFX::commitChanges()
{
// BGFX handles texture updates immediately
// No deferred commit needed
}
void TextureBGFX::generateMipmaps()
{
// BGFX generates mipmaps automatically when creating textures with mipLevels > 1
// Or we can request regeneration if needed
// Note: Not all backends support runtime mipmap generation
}
void TextureBGFX::bind(unsigned int stage, const SamplerState& state)
{
uint32_t samplerFlags = getBGFXSamplerFlags(state);
// In BGFX, textures are set per-draw call, not globally
// We just set the texture and sampler for the given stage
// Note: This requires a valid sampler uniform handle to be passed from the caller
bgfx::setTexture(stage, BGFX_INVALID_HANDLE, handle, samplerFlags);
cachedState = state;
}
void TextureBGFX::bindWithUniform(unsigned int stage, bgfx::UniformHandle samplerUniform, const SamplerState& state)
{
uint32_t samplerFlags = getBGFXSamplerFlags(state);
// Set texture with the proper sampler uniform
bgfx::setTexture(stage, samplerUniform, handle, samplerFlags);
cachedState = state;
}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,57 @@
#pragma once
#include "Core/Texture.hpp"
#include "Core/States.hpp"
#include <bgfx/bgfx.h>
#include <boost/enable_shared_from_this.hpp>
#include <map>
namespace Aya
{
namespace Graphics
{
class DeviceBGFX;
class TextureBGFX
: public Texture
, public boost::enable_shared_from_this<TextureBGFX>
{
public:
TextureBGFX(
Device* device, Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, Usage usage);
~TextureBGFX();
virtual void upload(unsigned int index, unsigned int mip, const TextureRegion& region, const void* data, unsigned int size);
virtual bool download(unsigned int index, unsigned int mip, void* data, unsigned int size);
virtual bool supportsLocking() const;
virtual LockResult lock(unsigned int index, unsigned int mip, const TextureRegion& region);
virtual void unlock(unsigned int index, unsigned int mip);
virtual shared_ptr<Renderbuffer> getRenderbuffer(unsigned int index, unsigned int mip);
virtual void commitChanges();
virtual void generateMipmaps();
void bind(unsigned int stage, const SamplerState& state);
void bindWithUniform(unsigned int stage, bgfx::UniformHandle samplerUniform, const SamplerState& state);
bgfx::TextureHandle getHandle() const
{
return handle;
}
static bgfx::TextureFormat::Enum getBGFXFormat(Format format);
private:
bgfx::TextureHandle handle;
SamplerState cachedState;
typedef std::map<std::pair<unsigned, unsigned>, weak_ptr<Renderbuffer>> RenderBufferMap;
RenderBufferMap renderBuffers;
};
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,201 @@
#include "ContextGL.hpp"
#include "HeadersGL.hpp"
#include "Debug.hpp"
#include <glad/egl.h>
#include <wayland-client.h>
#include <wayland-egl.h>
#include <X11/Xlib.h>
#include <EGL/eglext.h>
#include <cstdlib>
LOGGROUP(Graphics)
namespace Aya
{
namespace Graphics
{
enum class DisplayBackend
{
Wayland,
X11,
Unknown
};
static DisplayBackend detectDisplayBackend()
{
const char* waylandDisplay = std::getenv("WAYLAND_DISPLAY");
const char* x11Display = std::getenv("DISPLAY");
if (waylandDisplay && waylandDisplay[0])
return DisplayBackend::Wayland;
if (x11Display && x11Display[0])
return DisplayBackend::X11;
return DisplayBackend::Unknown;
}
class ContextEGL : public ContextGL
{
public:
ContextEGL(void* windowHandle, void* windowScreen, int w, int h)
{
DisplayBackend backend = detectDisplayBackend();
// Load core EGL API
gladLoaderLoadEGL(nullptr);
// Load required EGL extensions
auto eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
auto eglCreatePlatformWindowSurfaceEXT = (PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT");
if (!eglGetPlatformDisplayEXT || !eglCreatePlatformWindowSurfaceEXT)
throw Aya::runtime_error("Missing required EGL extension functions");
// Create EGL display based on backend
if (backend == DisplayBackend::Wayland)
{
wl_display* wlDisplay = static_cast<wl_display*>(windowScreen);
display = eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_EXT, wlDisplay, nullptr);
}
else if (backend == DisplayBackend::X11)
{
::Display* x11Display = static_cast<::Display*>(windowScreen);
display = eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT, x11Display, nullptr);
}
else
{
throw Aya::runtime_error("Unknown or unsupported display backend");
}
if (display == EGL_NO_DISPLAY)
throw Aya::runtime_error("eglGetPlatformDisplayEXT failed: %x", eglGetError());
if (!eglInitialize(display, nullptr, nullptr))
throw Aya::runtime_error("eglInitialize failed: %x", eglGetError());
if (!gladLoaderLoadEGL(display))
throw Aya::runtime_error("gladLoaderLoadEGL failed");
// EGL Config
static const EGLint configAttribs[] = {EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE,
8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 24, EGL_STENCIL_SIZE, 8, EGL_NONE};
EGLint numConfigs;
if (!eglChooseConfig(display, configAttribs, &config, 1, &numConfigs) || numConfigs == 0)
throw Aya::runtime_error("eglChooseConfig failed: %x", eglGetError());
// Surface creation
if (windowHandle)
{
if (backend == DisplayBackend::Wayland)
{
surface = eglCreatePlatformWindowSurfaceEXT(display, config, windowHandle, nullptr);
}
else // X11
{
surface = eglCreatePlatformWindowSurfaceEXT(display, config, windowHandle, nullptr);
}
}
else
{
// Create pbuffer
static const EGLint pbufferAttribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
surface = eglCreatePbufferSurface(display, config, pbufferAttribs);
}
if (surface == EGL_NO_SURFACE)
throw Aya::runtime_error("Failed to create EGL surface: %x", eglGetError());
// Bind OpenGL API
eglBindAPI(EGL_OPENGL_API);
static const EGLint contextAttribs[] = {EGL_CONTEXT_MAJOR_VERSION, 3, EGL_CONTEXT_MINOR_VERSION, 3, EGL_CONTEXT_OPENGL_PROFILE_MASK,
EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
#ifndef NDEBUG
EGL_CONTEXT_OPENGL_DEBUG, EGL_TRUE,
#endif
EGL_NONE};
context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
if (context == EGL_NO_CONTEXT)
throw Aya::runtime_error("eglCreateContext failed: %x", eglGetError());
if (!eglMakeCurrent(display, surface, surface, context))
throw Aya::runtime_error("eglMakeCurrent failed: %x", eglGetError());
gladLoaderLoadGL();
}
virtual void setCurrent() override
{
eglMakeCurrent(display, surface, surface, context);
}
virtual void swapBuffers() override
{
eglSwapBuffers(display, surface);
}
virtual unsigned int getMainFramebufferId() override
{
return 0;
}
virtual bool isMainFramebufferRetina() override
{
return false;
}
void resizeWindow(int w, int h)
{
if (egl_window)
{
wl_egl_window_resize(egl_window, w, h, 0, 0);
}
}
virtual std::pair<unsigned int, unsigned int> updateMainFramebuffer(unsigned int width, unsigned int height) override
{
EGLint w, h;
eglQuerySurface(display, surface, EGL_WIDTH, &w);
eglQuerySurface(display, surface, EGL_HEIGHT, &h);
return std::make_pair((unsigned int)w, (unsigned int)h);
}
virtual ~ContextEGL()
{
if (display != EGL_NO_DISPLAY)
{
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (context != EGL_NO_CONTEXT)
eglDestroyContext(display, context);
if (surface != EGL_NO_SURFACE)
eglDestroySurface(display, surface);
eglTerminate(display);
}
if (egl_window)
{
wl_egl_window_destroy(egl_window);
egl_window = nullptr;
}
}
private:
EGLDisplay display = EGL_NO_DISPLAY;
EGLContext context = EGL_NO_CONTEXT;
EGLSurface surface = EGL_NO_SURFACE;
EGLConfig config = nullptr;
wl_egl_window* egl_window = nullptr;
};
ContextGL* ContextGL::create(void* windowHandle, void* display, int w, int h)
{
return new ContextEGL(windowHandle, display, w, h);
}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,28 @@
#pragma once
#include <utility>
namespace Aya
{
namespace Graphics
{
class ContextGL
{
public:
static ContextGL* create(void* windowHandle, void* display, int w, int h);
virtual ~ContextGL() {}
virtual void setCurrent() = 0;
virtual void swapBuffers() = 0;
virtual unsigned int getMainFramebufferId() = 0;
virtual bool isMainFramebufferRetina() = 0;
virtual std::pair<unsigned int, unsigned int> updateMainFramebuffer(unsigned int width, unsigned int height) = 0;
virtual void resizeWindow(int width, int height) = 0;
};
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,286 @@
#include "ContextGL.hpp"
#include "HeadersGL.hpp"
#include "Debug.hpp"
#include <android/native_window.h>
#include <EGL/egl.h>
LOGGROUP(Graphics)
// GL extensions
static GLvoid(GL_APIENTRY* glBindVertexArrayPtr)(GLuint array);
static GLvoid(GL_APIENTRY* glDeleteVertexArraysPtr)(GLsizei n, const GLuint* arrays);
static GLvoid(GL_APIENTRY* glGenVertexArraysPtr)(GLsizei n, const GLuint* arrays);
static GLvoid*(GL_APIENTRY* glMapBufferPtr)(GLenum target, GLenum access);
static GLboolean(GL_APIENTRY* glUnmapBufferPtr)(GLenum target);
static GLvoid*(GL_APIENTRY* glMapBufferRangePtr)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
static GLvoid(GL_APIENTRY* glTexImage3DPtr)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth,
GLint border, GLenum format, GLenum type, const GLvoid* pixels);
static GLvoid(GL_APIENTRY* glTexSubImage3DPtr)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height,
GLsizei depth, GLenum format, GLenum type, const GLvoid* pixels);
static GLvoid(GL_APIENTRY* glTexStorage2DPtr)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
static GLvoid(GL_APIENTRY* glTexStorage3DPtr)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);
static GLsync(GL_APIENTRY* glFenceSyncPtr)(GLenum condition, GLbitfield flags);
static void(GL_APIENTRY* glDeleteSyncPtr)(GLsync sync);
static GLenum(GL_APIENTRY* glClientWaitSyncPtr)(GLsync sync, GLbitfield flags, GLuint64 timeout);
static void(GL_APIENTRY* glWaitSyncPtr)(GLsync sync, GLbitfield flags, GLuint64 timeout);
static GLvoid(GL_APIENTRY* glBlitFramebufferPtr)(
GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
static GLvoid(GL_APIENTRY* glRenderbufferStorageMultisamplePtr)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
static GLvoid(GL_APIENTRY* glInvalidateFramebufferPtr)(GLenum target, GLsizei numAttachments, const GLenum* attachments);
template<typename T>
static void loadExtensionGL(T& ptr, const char* namecore, const char* nameext)
{
if (!ptr)
ptr = reinterpret_cast<T>(eglGetProcAddress(namecore));
if (!ptr)
ptr = reinterpret_cast<T>(eglGetProcAddress(nameext));
}
#define LOAD_EXTENSION_GL(name, suffix) loadExtensionGL(name##Ptr, #name, #name #suffix)
// GL stubs
GLvoid glBindVertexArray(GLuint array)
{
glBindVertexArrayPtr(array);
}
GLvoid glDeleteVertexArrays(GLsizei n, const GLuint* arrays)
{
glDeleteVertexArraysPtr(n, arrays);
}
GLvoid glGenVertexArrays(GLsizei n, GLuint* arrays)
{
glGenVertexArraysPtr(n, arrays);
}
GLvoid* glMapBuffer(GLenum target, GLenum access)
{
return glMapBufferPtr(target, access);
}
GLboolean glUnmapBuffer(GLenum target)
{
return glUnmapBufferPtr(target);
}
GLvoid* glMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access)
{
return glMapBufferRangePtr(target, offset, length, access);
}
GLvoid glTexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
{
glTexStorage2DPtr(target, levels, internalformat, width, height);
}
GLvoid glTexImage3D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format,
GLenum type, const GLvoid* pixels)
{
glTexImage3DPtr(target, level, internalFormat, width, height, depth, border, format, type, pixels);
}
GLvoid glTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
GLenum format, GLenum type, const GLvoid* pixels)
{
glTexSubImage3DPtr(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
}
GLvoid glTexStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
{
glTexStorage3DPtr(target, levels, internalformat, width, height, depth);
}
GLsync glFenceSync(GLenum condition, GLbitfield flags)
{
return glFenceSyncPtr(condition, flags);
}
void glDeleteSync(GLsync sync)
{
glDeleteSyncPtr(sync);
}
GLenum glClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout)
{
return glClientWaitSyncPtr(sync, flags, timeout);
}
void glWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout)
{
glWaitSyncPtr(sync, flags, timeout);
}
GLvoid glBlitFramebuffer(
GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)
{
glBlitFramebufferPtr(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
}
GLvoid glRenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)
{
glRenderbufferStorageMultisamplePtr(target, samples, internalformat, width, height);
}
GLvoid glInvalidateFramebuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments)
{
glInvalidateFramebufferPtr(target, numAttachments, attachments);
}
namespace Aya
{
namespace Graphics
{
class ContextGLAndroid : public ContextGL
{
public:
ContextGLAndroid(void* windowHandle)
{
aNativeWindow = static_cast<ANativeWindow*>(windowHandle);
ANativeWindow_acquire(aNativeWindow);
FASTLOG2(FLog::Graphics, "Window size: %dx%d", ANativeWindow_getWidth(aNativeWindow), ANativeWindow_getHeight(aNativeWindow));
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (display == EGL_NO_DISPLAY)
throw runtime_error("Error creating context: eglGetDisplay %x", eglGetError());
if (!eglInitialize(display, 0, 0))
throw runtime_error("Error creating context: eglInitialize %x", eglGetError());
EGLConfig config;
if (!tryChooseConfig(display, &config, 8, 8, 8, 24, 0) && !tryChooseConfig(display, &config, 8, 8, 8, 16, 0) &&
!tryChooseConfig(display, &config, 5, 6, 5, 16, 0) && !tryChooseConfig(display, &config, 8, 8, 8, 24, 1) &&
!tryChooseConfig(display, &config, 8, 8, 8, 16, 1) && !tryChooseConfig(display, &config, 5, 6, 5, 16, 1))
throw runtime_error("Error creating context: could not find suitable config (%x)", eglGetError());
surface = eglCreateWindowSurface(display, config, aNativeWindow, 0);
if (!surface)
{
throw runtime_error("Error creating context: eglCreateWindowSurface %x", eglGetError());
}
static const EGLint contextAttrs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
context = eglCreateContext(display, config, NULL, contextAttrs);
if (!context)
throw runtime_error("Error creating context: eglCreateContext %x", eglGetError());
if (!eglMakeCurrent(display, surface, surface, context))
throw runtime_error("Error creating context: eglMakeCurrent %x", eglGetError());
eglQuerySurface(display, surface, EGL_WIDTH, &surfaceWidth);
eglQuerySurface(display, surface, EGL_HEIGHT, &surfaceHeight);
FASTLOG4(FLog::Graphics, "Initialized EGL context %p (surface %p) with renderbuffer %dx%d", context, surface, surfaceWidth, surfaceHeight);
EGLint minSwapInterval = -1;
eglGetConfigAttrib(display, config, EGL_MIN_SWAP_INTERVAL, &minSwapInterval);
FASTLOG1(FLog::Graphics, "EGL_MIN_SWAP_INTERVAL: %d", minSwapInterval);
if (eglSwapInterval(display, 0) == EGL_FALSE)
{
// This should be impossible as the interval is silently clamped to EGL_MIN_SWAP_INTERVAL.
FASTLOG(FLog::Graphics, "*** eglSwapInterval EGL_FALSE");
}
LOAD_EXTENSION_GL(glBindVertexArray, OES);
LOAD_EXTENSION_GL(glDeleteVertexArrays, OES);
LOAD_EXTENSION_GL(glGenVertexArrays, OES);
LOAD_EXTENSION_GL(glMapBuffer, OES);
LOAD_EXTENSION_GL(glUnmapBuffer, OES);
LOAD_EXTENSION_GL(glMapBufferRange, EXT);
LOAD_EXTENSION_GL(glTexImage3D, OES);
LOAD_EXTENSION_GL(glTexSubImage3D, OES);
LOAD_EXTENSION_GL(glTexStorage2D, EXT);
LOAD_EXTENSION_GL(glTexStorage3D, EXT);
LOAD_EXTENSION_GL(glFenceSync, EXT);
LOAD_EXTENSION_GL(glDeleteSync, EXT);
LOAD_EXTENSION_GL(glClientWaitSync, EXT);
LOAD_EXTENSION_GL(glWaitSync, EXT);
LOAD_EXTENSION_GL(glBlitFramebuffer, EXT);
LOAD_EXTENSION_GL(glRenderbufferStorageMultisample, EXT);
LOAD_EXTENSION_GL(glInvalidateFramebuffer, EXT);
}
~ContextGLAndroid()
{
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(display, context);
eglDestroySurface(display, surface);
eglTerminate(display);
ANativeWindow_release(aNativeWindow);
}
virtual void setCurrent()
{
bool result = eglMakeCurrent(display, surface, surface, context);
AYAASSERT(result);
}
virtual void swapBuffers()
{
bool result = eglSwapBuffers(display, surface);
AYAASSERT(result);
}
virtual unsigned int getMainFramebufferId()
{
return 0;
}
virtual bool isMainFramebufferRetina()
{
return false;
}
virtual void resizeWindow(int w, int h)
{
return;
}
virtual std::pair<unsigned int, unsigned int> updateMainFramebuffer(unsigned int width, unsigned int height)
{
return std::make_pair(surfaceWidth, surfaceHeight);
}
private:
ANativeWindow* aNativeWindow;
EGLDisplay display;
EGLSurface surface;
EGLContext context;
EGLint surfaceWidth;
EGLint surfaceHeight;
static bool tryChooseConfig(EGLDisplay display, EGLConfig* config, int redBits, int greenBits, int blueBits, int depthBits, int swapInterval)
{
const EGLint attribs[] = {EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_MIN_SWAP_INTERVAL, swapInterval, EGL_RED_SIZE, redBits, EGL_GREEN_SIZE,
greenBits, EGL_BLUE_SIZE, blueBits, EGL_DEPTH_SIZE, depthBits, EGL_NONE};
FASTLOG4(FLog::Graphics, "Trying to choose EGL config r%d g%d b%d d%d", redBits, greenBits, blueBits, depthBits);
EGLint numConfigs = 0;
return eglChooseConfig(display, attribs, config, 1, &numConfigs) && numConfigs > 0;
}
};
ContextGL* ContextGL::create(void* windowHandle, void* display, int w, int h)
{
return new ContextGLAndroid(windowHandle);
}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,177 @@
#include "ContextGL.hpp"
#include "HeadersGL.hpp"
#include "Debug.hpp"
#ifdef __APPLE__
#import <Cocoa/Cocoa.h>
#import <OpenGL/OpenGL.h>
#import <OpenGL/gl3.h>
#endif
LOGGROUP(Graphics)
namespace Aya
{
namespace Graphics
{
#ifdef __APPLE__
class ContextGLMacOS : public ContextGL
{
public:
ContextGLMacOS(void* windowHandle)
{
// Handle both NSWindow and NSView (Qt widgets)
NSView* view = (__bridge NSView*)windowHandle;
if (!view) {
printf("ERROR: Invalid window/view handle\n");
return;
}
// Determine if it's a window or view
NSWindow* window = nil;
NSView* targetView = view;
if ([view isKindOfClass:[NSWindow class]]) {
window = (NSWindow*)view;
targetView = [window contentView];
nsWindow = window;
} else {
// If it's a view, try to get its window
window = [view window];
targetView = view;
nsWindow = window;
}
if (!targetView) {
printf("ERROR: Could not get target view\n");
return;
}
printf("Creating native macOS OpenGL context...\n");
// Create OpenGL pixel format
NSOpenGLPixelFormatAttribute attrs[] = {
NSOpenGLPFADoubleBuffer,
NSOpenGLPFADepthSize, 24,
NSOpenGLPFAStencilSize, 8,
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFAAccelerated,
NSOpenGLPFANoRecovery,
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
0
};
pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
if (!pixelFormat) {
printf("Failed to create OpenGL pixel format\n");
return;
}
// Create OpenGL context
glContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
if (!glContext) {
printf("Failed to create OpenGL context\n");
return;
}
// Set the target view
[glContext setView:targetView];
// Make context current
[glContext makeCurrentContext];
printf("Context created and made current successfully\n");
// Test OpenGL
const GLubyte* version = glGetString(GL_VERSION);
if (version) {
printf("OpenGL Version: %s\n", version);
}
const GLubyte* vendor = glGetString(GL_VENDOR);
if (vendor) {
printf("OpenGL Vendor: %s\n", vendor);
}
const GLubyte* renderer = glGetString(GL_RENDERER);
if (renderer) {
printf("OpenGL Renderer: %s\n", renderer);
}
}
~ContextGLMacOS()
{
if (glContext) {
[NSOpenGLContext clearCurrentContext];
}
}
virtual void setCurrent() override
{
if (glContext) {
[glContext makeCurrentContext];
}
}
virtual void swapBuffers() override
{
if (glContext) {
[glContext flushBuffer];
}
}
virtual unsigned int getMainFramebufferId() override
{
return 0; // Default framebuffer on macOS
}
virtual bool isMainFramebufferRetina() override
{
if (!nsWindow) return false;
NSScreen* screen = [nsWindow screen];
if (!screen) return false;
return [screen backingScaleFactor] > 1.0;
}
virtual std::pair<unsigned int, unsigned int> updateMainFramebuffer(unsigned int width, unsigned int height) override
{
if (!nsWindow) {
return std::make_pair(width, height);
}
NSView* contentView = [nsWindow contentView];
if (!contentView) {
return std::make_pair(width, height);
}
NSRect contentRect = [contentView bounds];
NSRect backingRect = [contentView convertRectToBacking:contentRect];
return std::make_pair((unsigned int)backingRect.size.width,
(unsigned int)backingRect.size.height);
}
virtual void resizeWindow(int w, int h) override
{
return;
}
private:
NSWindow* nsWindow;
NSOpenGLContext* glContext;
NSOpenGLPixelFormat* pixelFormat;
};
#endif
ContextGL* ContextGL::create(void* windowHandle, void* display, int w, int h)
{
return new ContextGLMacOS(windowHandle);
}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,146 @@
#include "ContextGL.hpp"
#include "HeadersGL.hpp"
#include "Debug.hpp"
#include <Windows.h>
#include <glad/gl.h>
#include <glad/wgl.h>
#include "Utility/StandardOut.hpp"
FASTFLAG(DebugGraphicsGL)
namespace Aya
{
namespace Graphics
{
class ContextGLWin32 : public ContextGL
{
public:
ContextGLWin32(void* windowHandle)
: hwnd(reinterpret_cast<HWND>(windowHandle))
, hdc(GetDC(hwnd))
, hglrc(NULL)
{
PIXELFORMATDESCRIPTOR pfd = {};
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cAlphaBits = 8;
pfd.cDepthBits = 24;
pfd.cStencilBits = 8;
pfd.iLayerType = PFD_MAIN_PLANE;
int pf = ChoosePixelFormat(hdc, &pfd);
if (!pf)
throw Aya::runtime_error("Error choosing pixel format: %x", GetLastError());
if (!SetPixelFormat(hdc, pf, &pfd))
throw Aya::runtime_error("Error setting pixel format: %x", GetLastError());
hglrc = wglCreateContext(hdc);
if (!hglrc)
throw Aya::runtime_error("Error creating context: %x", GetLastError());
if (!wglMakeCurrent(hdc, hglrc))
throw Aya::runtime_error("Error changing context: %x", GetLastError());
if (!gladLoaderLoadWGL(hdc))
throw Aya::runtime_error("Failed to initialize GLAD WGL with hdc");
// Initialize GLAD
if (!gladLoaderLoadGL())
throw Aya::runtime_error("Failed to initialize GLAD");
if (GLAD_WGL_ARB_create_context)
{
wglMakeCurrent(NULL, NULL);
wglDeleteContext(hglrc);
const int attribs[] = {WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 2, 0, 0};
hglrc = wglCreateContextAttribsARB(hdc, NULL, attribs);
if (!hglrc)
throw Aya::runtime_error("Error creating context: %x", GetLastError());
if (!wglMakeCurrent(hdc, hglrc))
throw Aya::runtime_error("Error changing context: %x", GetLastError());
// Reinitialize GLAD with the new context
if (!gladLoaderLoadGL())
throw Aya::runtime_error("Failed to reinitialize GLAD");
}
if (GLAD_WGL_EXT_swap_control)
wglSwapIntervalEXT(0);
}
~ContextGLWin32()
{
if (wglGetCurrentContext() == hglrc)
wglMakeCurrent(NULL, NULL);
wglDeleteContext(hglrc);
}
virtual void setCurrent()
{
if (wglGetCurrentContext() != hglrc)
{
BOOL result = wglMakeCurrent(hdc, hglrc);
AYAASSERT(result);
}
}
virtual void swapBuffers()
{
AYAASSERT(wglGetCurrentContext() == hglrc);
SwapBuffers(hdc);
}
virtual unsigned int getMainFramebufferId()
{
return 0;
}
virtual bool isMainFramebufferRetina()
{
return false;
}
virtual std::pair<unsigned int, unsigned int> updateMainFramebuffer(unsigned int width, unsigned int height)
{
RECT rect = {};
GetClientRect(hwnd, &rect);
return std::make_pair(rect.right - rect.left, rect.bottom - rect.top);
}
virtual void resizeWindow(int w, int h)
{
return;
}
private:
HWND hwnd;
HDC hdc;
HGLRC hglrc;
};
ContextGL* ContextGL::create(void* windowHandle, void* display, int w, int h)
{
return new ContextGLWin32(windowHandle);
}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,513 @@
#include "DeviceGL.hpp"
#include "GeometryGL.hpp"
#include "ShaderGL.hpp"
#include "TextureGL.hpp"
#include "FramebufferGL.hpp"
#include "HeadersGL.hpp"
#ifdef AYA_OS_WINDOWS
#include <Windows.h>
#endif
FASTFLAGVARIABLE(GraphicsGLUseDiscard, false)
namespace Aya
{
namespace Graphics
{
static const GLenum gCullModeGL[RasterizerState::Cull_Count] = {GL_NONE, GL_BACK, GL_FRONT};
struct BlendFuncGL
{
GLenum src, dst;
};
static const GLenum gBlendFactorsGL[BlendState::Factor_Count] = {
GL_ONE, GL_ZERO, GL_DST_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA};
static const GLenum gDepthFuncGL[DepthState::Function_Count] = {GL_ALWAYS, GL_LESS, GL_LEQUAL};
DeviceContextGL::DeviceContextGL(DeviceGL* dev)
: globalDataVersion(0)
, device(dev)
, defaultAnisotropy(1)
, cachedProgram(0)
, cachedRasterizerState(RasterizerState::Cull_None)
, cachedBlendState(BlendState::Mode_None)
, cachedDepthState(DepthState::Function_Always, false)
{
for (size_t i = 0; i < ARRAYSIZE(cachedTextures); ++i)
cachedTextures[i] = NULL;
}
DeviceContextGL::~DeviceContextGL() {}
void DeviceContextGL::clearStates()
{
// Clear framebuffer cache
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Clear program cache
cachedProgram = 0;
glUseProgram(0);
// Clear texture cache
for (size_t i = 0; i < ARRAYSIZE(cachedTextures); ++i)
cachedTextures[i] = NULL;
// Clear states to invalid values to guarantee a cache miss on the next setup
cachedRasterizerState = RasterizerState(RasterizerState::Cull_Count);
cachedBlendState = BlendState(BlendState::Mode_Count);
cachedDepthState = DepthState(DepthState::Function_Count, false);
// Setup state we never touch once
#ifndef GLES
glEnable(GL_PROGRAM_POINT_SIZE);
#endif
}
void DeviceContextGL::invalidateCachedProgram()
{
cachedProgram = 0;
}
void DeviceContextGL::invalidateCachedTexture(Texture* texture)
{
for (unsigned int stage = 0; stage < ARRAYSIZE(cachedTextures); ++stage)
if (cachedTextures[stage] == texture)
cachedTextures[stage] = NULL;
}
void DeviceContextGL::invalidateCachedTextureStage(unsigned int stage)
{
AYAASSERT(stage < ARRAYSIZE(cachedTextures));
cachedTextures[stage] = NULL;
}
void DeviceContextGL::defineGlobalConstants(size_t dataSize)
{
AYAASSERT(globalData.empty());
AYAASSERT(dataSize > 0);
globalData.resize(dataSize);
}
void DeviceContextGL::setDefaultAnisotropy(unsigned int value)
{
defaultAnisotropy = value;
}
void DeviceContextGL::updateGlobalConstants(const void* data, size_t dataSize)
{
AYAASSERT(dataSize == globalData.size());
memcpy(&globalData[0], data, dataSize);
globalDataVersion++;
}
void DeviceContextGL::bindFramebuffer(Framebuffer* buffer)
{
unsigned int drawId = static_cast<FramebufferGL*>(buffer)->getId();
glBindFramebuffer(GL_FRAMEBUFFER, drawId);
glViewport(0, 0, buffer->getWidth(), buffer->getHeight());
#ifndef GLES
unsigned int drawBuffers = static_cast<FramebufferGL*>(buffer)->getDrawBuffers();
if (drawId == 0)
{
glDrawBuffer(GL_BACK);
}
else if (glDrawBuffers)
{
GLenum buffers[16];
AYAASSERT(drawBuffers < ARRAYSIZE(buffers));
for (unsigned int i = 0; i < drawBuffers; ++i)
buffers[i] = GL_COLOR_ATTACHMENT0 + i;
glDrawBuffers(drawBuffers, buffers);
}
#endif
}
void DeviceContextGL::clearFramebuffer(unsigned int mask, const float color[4], float depth, unsigned int stencil)
{
unsigned int maskGl = 0;
if (mask & Buffer_Color)
{
// Need color writes for color clear to work
setBlendState(BlendState(BlendState::Mode_None, BlendState::Color_All));
maskGl |= GL_COLOR_BUFFER_BIT;
glClearColor(color[0], color[1], color[2], color[3]);
}
if (mask & Buffer_Depth)
{
// Need depth writes for depth clear to work
setDepthState(DepthState(DepthState::Function_Always, true));
maskGl |= GL_DEPTH_BUFFER_BIT;
glClearDepth(depth);
}
if (mask & Buffer_Stencil)
{
// Need stencil writes for stencil clear to work
glStencilMask(~0u);
maskGl |= GL_STENCIL_BUFFER_BIT;
glClearStencil(stencil);
}
AYAASSERT(maskGl);
glClear(maskGl);
}
void DeviceContextGL::copyFramebuffer(Framebuffer* buffer, Texture* texture, int xOffset, int yOffset)
{
AYAASSERT(texture->getType() == Texture::Type_2D);
AYAASSERT(buffer->getWidth() == texture->getWidth() && buffer->getHeight() == texture->getHeight());
invalidateCachedTextureStage(0);
GLint oldfb = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldfb);
glBindFramebuffer(GL_FRAMEBUFFER, static_cast<FramebufferGL*>(buffer)->getId());
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, static_cast<TextureGL*>(texture)->getId());
#ifndef GLES
glReadBuffer(GL_COLOR_ATTACHMENT0);
#endif
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, -xOffset, yOffset, texture->getWidth(), texture->getHeight());
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, oldfb);
}
void DeviceContextGL::resolveFramebuffer(Framebuffer* msaaBuffer, Framebuffer* buffer, unsigned int mask)
{
AYAASSERT(msaaBuffer->getSamples() > 1);
AYAASSERT(buffer->getSamples() == 1);
AYAASSERT(msaaBuffer->getWidth() == buffer->getWidth() && msaaBuffer->getHeight() == buffer->getHeight());
GLint oldfb = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldfb);
glBindFramebuffer(GL_READ_FRAMEBUFFER, static_cast<FramebufferGL*>(msaaBuffer)->getId());
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, static_cast<FramebufferGL*>(buffer)->getId());
if (mask & Buffer_Color)
{
#ifndef GLES
glReadBuffer(GL_COLOR_ATTACHMENT0);
#endif
glBlitFramebuffer(
0, 0, buffer->getWidth(), buffer->getHeight(), 0, 0, buffer->getWidth(), buffer->getHeight(), GL_COLOR_BUFFER_BIT, GL_LINEAR);
}
if (mask & (Buffer_Depth | Buffer_Stencil))
{
unsigned int maskGl = 0;
if (mask & Buffer_Depth)
maskGl |= GL_DEPTH_BUFFER_BIT;
if (mask & Buffer_Stencil)
maskGl |= GL_STENCIL_BUFFER_BIT;
glBlitFramebuffer(0, 0, buffer->getWidth(), buffer->getHeight(), 0, 0, buffer->getWidth(), buffer->getHeight(), maskGl, GL_NEAREST);
}
glBindFramebuffer(GL_FRAMEBUFFER, oldfb);
}
void DeviceContextGL::discardFramebuffer(Framebuffer* buffer, unsigned int mask)
{
if (!device->getCapsGL().ext3)
return;
if (!FFlag::GraphicsGLUseDiscard)
return;
unsigned int drawBuffers = static_cast<FramebufferGL*>(buffer)->getDrawBuffers();
GLenum attachments[16];
unsigned int index = 0;
if (mask & Buffer_Color)
for (unsigned int i = 0; i < drawBuffers; ++i)
attachments[index++] = GL_COLOR_ATTACHMENT0 + i;
if (mask & Buffer_Depth)
attachments[index++] = GL_DEPTH_ATTACHMENT;
if (mask & Buffer_Stencil)
attachments[index++] = GL_STENCIL_ATTACHMENT;
if (index > 0)
{
GLint oldfb = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldfb);
glBindFramebuffer(GL_FRAMEBUFFER, static_cast<FramebufferGL*>(buffer)->getId());
glInvalidateFramebuffer(GL_FRAMEBUFFER, index, attachments);
glBindFramebuffer(GL_FRAMEBUFFER, oldfb);
}
}
void DeviceContextGL::bindProgram(ShaderProgram* program)
{
static_cast<ShaderProgramGL*>(program)->bind(&globalData[0], globalDataVersion, &cachedProgram);
}
void DeviceContextGL::setWorldTransforms4x3(const float* data, size_t matrixCount)
{
cachedProgram->setWorldTransforms4x3(data, matrixCount);
}
void DeviceContextGL::setConstant(int handle, const float* data, size_t vectorCount)
{
cachedProgram->setConstant(handle, data, vectorCount);
}
void DeviceContextGL::bindTexture(unsigned int stage, Texture* texture, const SamplerState& state)
{
SamplerState realState = (state.getFilter() == SamplerState::Filter_Anisotropic && state.getAnisotropy() == 0)
? SamplerState(state.getFilter(), state.getAddress(), defaultAnisotropy)
: state;
AYAASSERT(stage < device->getCaps().maxTextureUnits);
AYAASSERT(stage < ARRAYSIZE(cachedTextures));
static_cast<TextureGL*>(texture)->bind(stage, realState, &cachedTextures[stage]);
}
void DeviceContextGL::setRasterizerState(const RasterizerState& state)
{
if (cachedRasterizerState != state)
{
cachedRasterizerState = state;
if (state.getCullMode() == RasterizerState::Cull_None)
{
glDisable(GL_CULL_FACE);
}
else
{
glEnable(GL_CULL_FACE);
glCullFace(gCullModeGL[state.getCullMode()]);
}
if (state.getDepthBias() == 0)
{
glDisable(GL_POLYGON_OFFSET_FILL);
}
else
{
float bias = static_cast<float>(state.getDepthBias());
float slopeBias = bias / 32.f; // do we need explicit control over slope or just a better formula since these numbers are magic anyway?
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(slopeBias, bias);
}
}
}
void DeviceContextGL::setBlendState(const BlendState& state)
{
if (cachedBlendState != state)
{
cachedBlendState = state;
unsigned int colorMask = state.getColorMask();
glColorMask((colorMask & BlendState::Color_R) != 0, (colorMask & BlendState::Color_G) != 0, (colorMask & BlendState::Color_B) != 0,
(colorMask & BlendState::Color_A) != 0);
if (!state.blendingNeeded())
{
glDisable(GL_BLEND);
}
else
{
glEnable(GL_BLEND);
if (!state.separateAlphaBlend())
glBlendFunc(gBlendFactorsGL[state.getColorSrc()], gBlendFactorsGL[state.getColorDst()]);
else
glBlendFuncSeparate(gBlendFactorsGL[state.getColorSrc()], gBlendFactorsGL[state.getColorDst()], gBlendFactorsGL[state.getAlphaSrc()],
gBlendFactorsGL[state.getAlphaDst()]);
}
}
}
void DeviceContextGL::setDepthState(const DepthState& state)
{
if (cachedDepthState != state)
{
cachedDepthState = state;
if (state.getFunction() != DepthState::Function_None && state.getFunction() == DepthState::Function_Always && !state.getWrite())
{
glDisable(GL_DEPTH_TEST);
}
else
{
if (state.getFunction() != DepthState::Function_None)
{
glEnable(GL_DEPTH_TEST);
glDepthFunc(gDepthFuncGL[state.getFunction()]);
glDepthMask(state.getWrite());
}
}
switch (state.getStencilMode())
{
case DepthState::Stencil_None:
glDisable(GL_STENCIL_TEST);
break;
case DepthState::Stencil_IsNotZero:
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_NOTEQUAL, 0, ~0u);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
break;
case DepthState::Stencil_UpdateZFail:
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 0, ~0u);
glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_DECR_WRAP, GL_KEEP);
glStencilOpSeparate(GL_BACK, GL_KEEP, GL_INCR_WRAP, GL_KEEP);
break;
case DepthState::Stencil_Increment:
// disable writing to color & depth buffers
// clear stencil buffer
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glClear(GL_STENCIL_BUFFER_BIT);
glDepthMask(GL_FALSE);
glEnable(GL_DEPTH_CLAMP);
// enable face culling
glEnable(GL_CULL_FACE);
// enable stencil testing, always pass
glEnable(GL_STENCIL_TEST);
glEnable(GL_DEPTH_TEST);
glStencilMask(~0u);
// (?) not sure what exactly this is supposed to do, added based on analysis of the framebuffer of the graphics pipeline in older clients
glDepthFunc(GL_LESS);
// increase for back faces, decrease stencil bit for front faces
glCullFace(GL_FRONT);
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP);
glStencilFunc(GL_ALWAYS, 0, ~0u);
break;
case DepthState::Stencil_Decrement:
glCullFace(GL_BACK);
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR_WRAP);
glStencilFunc(GL_ALWAYS, 0, ~0u);
break;
case DepthState::Stencil_IsNotZeroReplace:
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_CLAMP);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDepthMask(GL_FALSE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_LESS, 0x01, ~0u);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glDepthFunc(GL_LEQUAL);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
break;
default:
AYAASSERT(false);
}
}
}
void DeviceContextGL::drawImpl(Geometry* geometry, Geometry::Primitive primitive, unsigned int offset, unsigned int count,
unsigned int indexRangeBegin, unsigned int indexRangeEnd)
{
static_cast<GeometryGL*>(geometry)->draw(primitive, offset, count);
}
//////////////////////////////////////////////////////////////////////////
// Markers:
void DeviceContextGL::pushDebugMarkerGroup(const char* text)
{
#ifdef AYA_PLATFORM_IOS
if (device->getCapsGL().extDebugMarkers)
{
glPushGroupMarkerEXT(0, text);
}
#elif defined(__ANDROID__)
;
#else
if (glPushDebugGroup) // Requires GL4.3, because ARB-version does not have marker enums for type
{
glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION_ARB, 0, -1, text);
}
#endif
}
void DeviceContextGL::popDebugMarkerGroup()
{
#ifdef AYA_PLATFORM_IOS
if (device->getCapsGL().extDebugMarkers)
{
glPopGroupMarkerEXT();
}
#elif defined(__ANDROID__)
;
#else
if (glPopDebugGroup) // Requires GL4.3, because ARB-version does not have marker enums for type
{
glPopDebugGroup();
}
#endif
}
void DeviceContextGL::setDebugMarker(const char* text)
{
#ifdef AYA_PLATFORM_IOS
if (device->getCapsGL().extDebugMarkers)
{
glInsertEventMarkerEXT(0, text);
}
#elif defined(__ANDROID__)
;
#else
if (glDebugMessageInsert) // Requires GL4.3, because ARB-version does not have marker enums for type
{
glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION_ARB, GL_DEBUG_TYPE_MARKER, 0, GL_DEBUG_SEVERITY_NOTIFICATION, -1, text);
}
#endif
}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,617 @@
#include "DeviceGL.hpp"
#include "GeometryGL.hpp"
#include "ShaderGL.hpp"
#include "TextureGL.hpp"
#include "FramebufferGL.hpp"
#include "ContextGL.hpp"
#include "HeadersGL.hpp"
#include "Profiler.hpp"
#include "ImGui.hpp"
#include <set>
#include <sstream>
#if defined(__linux) || defined(__APPLE__)
// https://stackoverflow.com/questions/33257597/segmentation-fault-in-c-linux-but-not-in-windows
#include <string.h>
#endif
LOGGROUP(Graphics)
FASTFLAGVARIABLE(DebugGraphicsGL, false)
FASTFLAGVARIABLE(GraphicsGL3, true)
FASTFLAGVARIABLE(GraphicsGLReduceLatency, true)
namespace Aya
{
namespace Graphics
{
static std::set<std::string> getExtensions()
{
std::set<std::string> result;
if (const char* extensionString = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)))
{
std::istringstream ext(extensionString);
std::string str;
while (ext >> str)
result.insert(str);
return result;
}
#ifndef GLES
if (FFlag::GraphicsGL3 && GLAD_GL_VERSION_3_0)
{
int extensionCount = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &extensionCount);
for (int i = 0; i < extensionCount; ++i)
result.insert(reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, i)));
}
#endif
return result;
}
#ifdef GLES
static DeviceCapsGL createDeviceCaps(ContextGL* context, const std::set<std::string>& extensions)
{
const char* renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
const char* version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
bool version3 = FFlag::GraphicsGL3 && strncmp(version, "OpenGL ES ", 10) == 0 && version[10] >= '3';
DeviceCapsGL caps;
GLint texSize;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize);
GLint stencilBits;
glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
caps.supportsFramebuffer = true;
caps.supportsShaders = true;
caps.supportsFFP = false;
caps.supportsStencil = stencilBits >= 8;
caps.supportsIndex32 = version3 || extensions.count("GL_OES_element_index_uint");
caps.supportsTextureDXT = false;
caps.supportsTexturePVR = extensions.count("GL_IMG_texture_compression_pvrtc");
caps.supportsTextureHalfFloat = version3 || extensions.count("GL_OES_texture_half_float");
caps.supportsTexture3D = version3;
caps.supportsTextureNPOT = version3;
caps.supportsTextureETC1 = extensions.count("GL_OES_compressed_ETC1_RGB8_texture");
caps.supportsTexturePartialMipChain =
version3 || extensions.count("GL_APPLE_texture_max_level") || (!FFlag::GraphicsGL3 && strstr(version, "OpenGL ES 3"));
caps.maxDrawBuffers = 1;
if (version3)
{
GLint samples = 1;
glGetIntegerv(GL_MAX_SAMPLES, &samples);
caps.maxSamples = samples;
}
else
{
caps.maxSamples = 1;
}
caps.maxTextureSize = texSize;
caps.maxTextureUnits = version3 ? 16 : 8;
caps.colorOrderBGR = false;
caps.needsHalfPixelOffset = false;
caps.requiresRenderTargetFlipping = true;
caps.retina = context->isMainFramebufferRetina();
caps.ext3 = version3;
caps.extVertexArrayObject = version3 || (extensions.count("GL_OES_vertex_array_object") && !strstr(renderer, "Adreno"));
caps.extTextureStorage = version3 || extensions.count("GL_EXT_texture_storage");
caps.extMapBuffer = version3 || extensions.count("GL_OES_mapbuffer");
caps.extMapBufferRange = version3 || extensions.count("GL_EXT_map_buffer_range");
caps.extTimerQuery = false;
caps.extDebugMarkers = extensions.count("GL_EXT_debug_marker");
caps.extSync = version3;
return caps;
}
#else
static DeviceCapsGL createDeviceCapsOld(ContextGL* context)
{
DeviceCapsGL caps;
GLint texSize = 0;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize);
GLint stencilBits = 0;
glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
caps.supportsFramebuffer = GLAD_GL_VERSION_3_0 || GLAD_GL_ARB_framebuffer_object || GLAD_GL_EXT_framebuffer_object;
caps.supportsShaders = GLAD_GL_VERSION_2_0 || (GLAD_GL_ARB_shading_language_100 && GLAD_GL_ARB_shader_objects && GLAD_GL_ARB_fragment_shader &&
GLAD_GL_ARB_vertex_shader);
caps.supportsFFP = false;
caps.supportsStencil = GLAD_GL_VERSION_2_0 && stencilBits >= 8; // we need full two-sided stencil support
caps.supportsIndex32 = true;
caps.supportsTextureDXT = (GLAD_GL_VERSION_1_3 || GLAD_GL_ARB_texture_compression) && GLAD_GL_EXT_texture_compression_s3tc;
caps.supportsTexturePVR = false;
caps.supportsTextureHalfFloat = !!GLAD_GL_ARB_half_float_pixel;
caps.supportsTexture3D = GLAD_GL_VERSION_1_2 || GLAD_GL_EXT_texture3D;
caps.supportsTextureETC1 = false;
caps.supportsTexturePartialMipChain = true;
// According to http://aras-p.info/blog/2012/10/17/non-power-of-two-textures/, GL extensions lie
caps.supportsTextureNPOT = GLAD_GL_ARB_texture_non_power_of_two && texSize >= 8192;
if (caps.supportsFramebuffer && (GLAD_GL_VERSION_3_0 || GLAD_GL_ARB_draw_buffers || GLAD_GL_ATI_draw_buffers))
{
GLint drawBuffers = 0;
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &drawBuffers);
caps.maxDrawBuffers = drawBuffers;
}
else
{
caps.maxDrawBuffers = 1;
}
if (caps.supportsFramebuffer && (GLAD_GL_VERSION_3_3 || GLAD_GL_EXT_framebuffer_multisample))
{
GLint samples = 0;
glGetIntegerv(GL_MAX_SAMPLES, &samples);
caps.maxSamples = samples;
}
else
{
caps.maxSamples = 1;
}
caps.maxTextureSize = texSize;
caps.maxTextureUnits = 16;
caps.colorOrderBGR = false;
caps.needsHalfPixelOffset = false;
caps.requiresRenderTargetFlipping = true;
caps.retina = context->isMainFramebufferRetina();
caps.ext3 = false;
caps.extVertexArrayObject = (GLAD_GL_VERSION_3_0 || GLAD_GL_ARB_vertex_array_object || GLAD_GL_APPLE_vertex_array_object);
caps.extTextureStorage = GLAD_GL_ARB_texture_storage;
caps.extMapBuffer = true;
caps.extMapBufferRange = (GLAD_GL_VERSION_3_0 || GLAD_GL_ARB_map_buffer_range);
caps.extTimerQuery = !!GLAD_GL_EXT_timer_query;
caps.extDebugMarkers = false;
caps.extSync = (GLAD_GL_VERSION_3_2 || GLAD_GL_ARB_sync);
#ifdef _WIN32
const char* vendorString = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
if (strstr(vendorString, "Intel"))
{
// Intel drivers don't store IB state as part of VAO (http://stackoverflow.com/questions/8973690/vao-and-element-array-buffer-state)
caps.extVertexArrayObject = false;
}
if (strstr(vendorString, "ATI Technologies"))
{
// Some AMD drivers can't create cubemaps with TS and can create 3D textures that they can't update afterwards...
caps.extTextureStorage = false;
}
#endif
return caps;
}
static DeviceCapsGL createDeviceCaps(ContextGL* context, const std::set<std::string>& extensions)
{
// if (!FFlag::GraphicsGL3)
// return createDeviceCapsOld(context);
const char* version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
bool version3 = isdigit(version[0]) && version[0] >= '3';
DeviceCapsGL caps;
GLint texSize = 0;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize);
GLint stencilBits = 0;
glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
caps.supportsFramebuffer = version3 || (extensions.count("GL_ARB_framebuffer_object") || extensions.count("GL_EXT_framebuffer_object"));
caps.supportsShaders = version3 || GLAD_GL_VERSION_2_0 ||
// need this for Mac OpenGL 1.4 :-/
(extensions.count("GL_ARB_shading_language_100") && extensions.count("GL_ARB_shader_objects") &&
extensions.count("GL_ARB_fragment_shader") && extensions.count("GL_ARB_vertex_shader"));
caps.supportsFFP = false;
caps.supportsStencil = GLAD_GL_VERSION_2_0 && stencilBits >= 8; // we need full two-sided stencil support
caps.supportsIndex32 = true;
caps.supportsTextureDXT = !!extensions.count("GL_EXT_texture_compression_s3tc");
caps.supportsTexturePVR = false;
caps.supportsTextureHalfFloat = version3;
caps.supportsTexture3D = true;
caps.supportsTextureETC1 = false;
caps.supportsTexturePartialMipChain = true;
caps.supportsTextureNPOT = version3;
if (version3)
{
GLint drawBuffers = 0;
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &drawBuffers);
GLint samples = 0;
glGetIntegerv(GL_MAX_SAMPLES, &samples);
caps.maxDrawBuffers = drawBuffers;
caps.maxSamples = samples;
}
else
{
caps.maxDrawBuffers = 1;
caps.maxSamples = 1;
}
caps.maxTextureSize = texSize;
caps.maxTextureUnits = 16;
caps.colorOrderBGR = false;
caps.needsHalfPixelOffset = false;
caps.requiresRenderTargetFlipping = true;
caps.retina = context->isMainFramebufferRetina();
caps.ext3 = version3;
caps.extVertexArrayObject = version3;
caps.extTextureStorage = GLAD_GL_ARB_texture_storage || (version3 && extensions.count("GL_ARB_texture_storage"));
caps.extMapBuffer = true;
caps.extMapBufferRange = version3;
caps.extTimerQuery = !!GLAD_GL_VERSION_3_3;
caps.extDebugMarkers = false;
caps.extSync = !!GLAD_GL_VERSION_3_2;
return caps;
}
static void GLAPIENTRY debugOutputGLARB(
GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const GLvoid* userParam)
{
FASTLOGS(FLog::Graphics, "GL Debug: %s", message);
}
#endif
DeviceGL::DeviceGL(void* windowHandle, void* display, int w, int h)
: frameTimeQueryId(0)
, frameTimeQueryIssued(false)
, frameEventQueryId(0)
, frameEventQueryIssued(false)
, gpuTime(0)
{
glContext.reset(ContextGL::create(windowHandle, display, w, h));
std::set<std::string> extensions = getExtensions();
caps = createDeviceCaps(glContext.get(), extensions);
FASTLOGS(FLog::Graphics, "GL Renderer: %s", reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
FASTLOGS(FLog::Graphics, "GL Version: %s", reinterpret_cast<const char*>(glGetString(GL_VERSION)));
FASTLOGS(FLog::Graphics, "GL Vendor: %s", reinterpret_cast<const char*>(glGetString(GL_VENDOR)));
if (caps.supportsShaders)
FASTLOGS(FLog::Graphics, "GLSL version: %s", reinterpret_cast<const char*>(glGetString(GL_SHADING_LANGUAGE_VERSION)));
std::string extensionString;
for (auto& e : extensions)
{
extensionString += " ";
extensionString += e;
}
while (!extensionString.empty())
{
std::string::size_type space = extensionString.find_first_of(' ', 128);
FASTLOGS(FLog::Graphics, "Ext:%s", extensionString.substr(0, space));
extensionString.erase(0, space);
}
caps.dumpToFLog(FLog::Graphics);
FASTLOG1(FLog::Graphics, "Caps: GL3 %d", caps.ext3);
FASTLOG5(FLog::Graphics, "Caps: VAO %d TexStorage %d MapBuffer %d MapBufferRange %d TimerQuery %d", caps.extVertexArrayObject,
caps.extTextureStorage, caps.extMapBuffer, caps.extMapBufferRange, caps.extTimerQuery);
FASTLOG2(FLog::Graphics, "Caps: DebugMarkers %d Sync %d", caps.extDebugMarkers, caps.extSync);
immediateContext.reset(new DeviceContextGL(this));
mainFramebuffer.reset(new FramebufferGL(this, glContext->getMainFramebufferId()));
std::pair<unsigned int, unsigned int> dimensions = glContext->updateMainFramebuffer(0, 0);
mainFramebuffer->updateDimensions(dimensions.first, dimensions.second);
#ifndef GLES
if (caps.extTimerQuery)
{
glGenQueries(1, &frameTimeQueryId);
AYAASSERT(frameTimeQueryId);
}
#endif
#if defined(_WIN32) || defined(__linux__)
Profiler::gpuInit(0);
#endif
ImGui::gpuInit();
}
DeviceGL::~DeviceGL()
{
glFinish();
glContext.reset();
#if defined(_WIN32) || defined(__linux__)
Profiler::gpuShutdown();
#endif
ImGui::gpuShutdown();
immediateContext.reset();
mainFramebuffer.reset();
}
void DeviceGL::resize(int w, int h)
{
glContext->resizeWindow(w, h);
}
std::string DeviceGL::getFeatureLevel()
{
std::string glString = reinterpret_cast<const char*>(glGetString(GL_VERSION));
std::string featureLevel = "OpenGL ";
if (!glString.empty())
{
size_t dotId = glString.find('.');
// this is based on GLEW implementation of determining GL version. Does it look unsafe? Dont worry, GLEW version is even worse;)
if (dotId > 0 && dotId < (glString.length() - 1) && isdigit(glString[dotId - 1]) && isdigit(glString[dotId + 1]))
{
featureLevel += glString[dotId - 1];
featureLevel += '.';
featureLevel += glString[dotId + 1];
return featureLevel;
}
}
featureLevel += "unknown";
return featureLevel;
}
bool DeviceGL::validate()
{
glContext->setCurrent();
std::pair<unsigned int, unsigned int> dimensions = glContext->updateMainFramebuffer(mainFramebuffer->getWidth(), mainFramebuffer->getHeight());
mainFramebuffer->updateDimensions(dimensions.first, dimensions.second);
return true;
}
DeviceContext* DeviceGL::beginFrame()
{
glContext->setCurrent();
#ifndef GLES
if (frameTimeQueryId && !frameTimeQueryIssued)
{
glBeginQuery(GL_TIME_ELAPSED, frameTimeQueryId);
}
#endif
immediateContext->bindFramebuffer(mainFramebuffer.get());
immediateContext->clearStates();
return immediateContext.get();
}
void DeviceGL::endFrame()
{
#ifndef GLES
if (frameTimeQueryId)
{
if (!frameTimeQueryIssued)
{
glEndQuery(GL_TIME_ELAPSED);
frameTimeQueryIssued = true;
}
else
{
int available = 0;
glGetQueryObjectiv(frameTimeQueryId, GL_QUERY_RESULT_AVAILABLE, &available);
if (available)
{
GLuint64EXT elapsed = 0;
if (FFlag::GraphicsGL3)
glGetQueryObjectui64v(frameTimeQueryId, GL_QUERY_RESULT, &elapsed);
else
glGetQueryObjectui64vEXT(frameTimeQueryId, GL_QUERY_RESULT, &elapsed);
gpuTime = static_cast<float>(elapsed) / 1e6f;
frameTimeQueryIssued = false;
}
}
}
#endif
if (FFlag::GraphicsGLReduceLatency && caps.extSync)
{
// Wait for last frame to finish on GPU
if (frameEventQueryIssued)
{
int rc = glClientWaitSync(frameEventQueryId, GL_SYNC_FLUSH_COMMANDS_BIT, GLuint64(5e9));
AYAASSERT(rc == GL_CONDITION_SATISFIED || rc == GL_ALREADY_SIGNALED);
glDeleteSync(frameEventQueryId);
frameEventQueryId = 0;
frameEventQueryIssued = false;
}
// Submit new query for frame wait
if (!frameEventQueryIssued)
{
frameEventQueryId = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
AYAASSERT(frameEventQueryId);
frameEventQueryIssued = true;
}
}
glContext->swapBuffers();
}
Framebuffer* DeviceGL::getMainFramebuffer()
{
return mainFramebuffer.get();
}
void DeviceGL::defineGlobalConstants(size_t dataSize, const std::vector<ShaderGlobalConstant>& constants)
{
AYAASSERT(globalConstants.empty());
AYAASSERT(!constants.empty());
globalConstants = constants;
immediateContext->defineGlobalConstants(dataSize);
}
std::string DeviceGL::getShadingLanguage()
{
#ifdef GLES
return caps.ext3 ? "glsles3" : "glsles";
#else
return caps.ext3 ? "glsl3" : "glsl3";
#endif
}
std::string DeviceGL::createShaderSource(
const std::string& path, const std::string& defines, boost::function<std::string(const std::string&)> fileCallback)
{
// No preprocessor support
return fileCallback(path);
}
std::vector<char> DeviceGL::createShaderBytecode(const std::string& source, const std::string& target, const std::string& entrypoint)
{
// No bytecode support
AYAASSERT(entrypoint == "main");
return std::vector<char>(source.begin(), source.end());
}
shared_ptr<VertexShader> DeviceGL::createVertexShader(const std::vector<char>& bytecode)
{
if (!caps.supportsShaders)
throw Aya::runtime_error("No shader support");
std::string source(bytecode.begin(), bytecode.end());
return shared_ptr<VertexShader>(new VertexShaderGL(this, source));
}
shared_ptr<FragmentShader> DeviceGL::createFragmentShader(const std::vector<char>& bytecode)
{
if (!caps.supportsShaders)
throw Aya::runtime_error("No shader support");
std::string source(bytecode.begin(), bytecode.end());
return shared_ptr<FragmentShader>(new FragmentShaderGL(this, source));
}
shared_ptr<ShaderProgram> DeviceGL::createShaderProgram(
const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader)
{
if (!caps.supportsShaders)
throw Aya::runtime_error("No shader support");
return shared_ptr<ShaderProgram>(new ShaderProgramGL(this, vertexShader, fragmentShader));
}
shared_ptr<ShaderProgram> DeviceGL::createShaderProgramFFP()
{
throw Aya::runtime_error("No FFP support");
}
shared_ptr<VertexBuffer> DeviceGL::createVertexBuffer(size_t elementSize, size_t elementCount, VertexBuffer::Usage usage)
{
return shared_ptr<VertexBuffer>(new VertexBufferGL(this, elementSize, elementCount, usage));
}
shared_ptr<IndexBuffer> DeviceGL::createIndexBuffer(size_t elementSize, size_t elementCount, VertexBuffer::Usage usage)
{
return shared_ptr<IndexBuffer>(new IndexBufferGL(this, elementSize, elementCount, usage));
}
shared_ptr<VertexLayout> DeviceGL::createVertexLayout(const std::vector<VertexLayout::Element>& elements)
{
return shared_ptr<VertexLayout>(new VertexLayoutGL(this, elements));
}
shared_ptr<Texture> DeviceGL::createTexture(Texture::Type type, Texture::Format format, unsigned int width, unsigned int height, unsigned int depth,
unsigned int mipLevels, Texture::Usage usage)
{
return shared_ptr<Texture>(new TextureGL(this, type, format, width, height, depth, mipLevels, usage));
}
shared_ptr<Renderbuffer> DeviceGL::createRenderbuffer(Texture::Format format, unsigned int width, unsigned int height, unsigned int samples)
{
return shared_ptr<Renderbuffer>(new RenderbufferGL(this, format, width, height, samples));
}
shared_ptr<Geometry> DeviceGL::createGeometryImpl(const shared_ptr<VertexLayout>& layout, const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers,
const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex)
{
return shared_ptr<Geometry>(new GeometryGL(this, layout, vertexBuffers, indexBuffer, baseVertexIndex));
}
shared_ptr<Framebuffer> DeviceGL::createFramebufferImpl(const std::vector<shared_ptr<Renderbuffer>>& color, const shared_ptr<Renderbuffer>& depth)
{
return shared_ptr<Framebuffer>(new FramebufferGL(this, color, depth));
}
DeviceStats DeviceGL::getStatistics() const
{
DeviceStats result = {};
result.gpuFrameTime = gpuTime;
return result;
}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,178 @@
#pragma once
#include "Core/Device.hpp"
#include "Core/States.hpp"
typedef struct __GLsync* GLsync;
namespace Aya
{
namespace Graphics
{
class FramebufferGL;
class ShaderProgramGL;
class TextureGL;
class ContextGL;
class DeviceGL;
struct DeviceCapsGL : DeviceCaps
{
bool ext3;
bool extVertexArrayObject;
bool extTextureStorage;
bool extMapBuffer;
bool extMapBufferRange;
bool extTimerQuery;
bool extDebugMarkers;
bool extSync;
};
class DeviceContextGL : public DeviceContext
{
public:
DeviceContextGL(DeviceGL* dev);
~DeviceContextGL();
void defineGlobalConstants(size_t dataSize);
void clearStates();
void invalidateCachedProgram();
void invalidateCachedTexture(Texture* texture);
void invalidateCachedTextureStage(unsigned int stage);
virtual void setDefaultAnisotropy(unsigned int value);
virtual void updateGlobalConstants(const void* data, size_t dataSize);
virtual void bindFramebuffer(Framebuffer* buffer);
virtual void clearFramebuffer(unsigned int mask, const float color[4], float depth, unsigned int stencil);
virtual void copyFramebuffer(Framebuffer* buffer, Texture* texture, int xOffset, int yOffset);
virtual void resolveFramebuffer(Framebuffer* msaaBuffer, Framebuffer* buffer, unsigned int mask);
virtual void discardFramebuffer(Framebuffer* buffer, unsigned int mask);
virtual void bindProgram(ShaderProgram* program);
virtual void setWorldTransforms4x3(const float* data, size_t matrixCount);
virtual void setConstant(int handle, const float* data, size_t vectorCount);
virtual void bindTexture(unsigned int stage, Texture* texture, const SamplerState& state);
virtual void setRasterizerState(const RasterizerState& state);
virtual void setBlendState(const BlendState& state);
virtual void setDepthState(const DepthState& state);
virtual void drawImpl(Geometry* geometry, Geometry::Primitive primitive, unsigned int offset, unsigned int count, unsigned int indexRangeBegin,
unsigned int indexRangeEnd);
virtual void pushDebugMarkerGroup(const char* text);
virtual void popDebugMarkerGroup();
virtual void setDebugMarker(const char* text);
private:
std::vector<char> globalData;
unsigned int globalDataVersion;
unsigned int defaultAnisotropy;
ShaderProgramGL* cachedProgram;
TextureGL* cachedTextures[16];
RasterizerState cachedRasterizerState;
BlendState cachedBlendState;
DepthState cachedDepthState;
DeviceGL* device;
};
class DeviceGL : public Device
{
public:
DeviceGL(void* windowHandle, void* display, int w, int h);
~DeviceGL();
virtual bool validate();
virtual void resize(int w, int h);
virtual DeviceContext* beginFrame();
virtual void endFrame();
virtual Framebuffer* getMainFramebuffer();
virtual void defineGlobalConstants(size_t dataSize, const std::vector<ShaderGlobalConstant>& constants);
virtual std::string getAPIName()
{
return "OpenGL";
}
virtual std::string getFeatureLevel();
virtual std::string getShadingLanguage();
virtual std::string createShaderSource(
const std::string& path, const std::string& defines, boost::function<std::string(const std::string&)> fileCallback);
virtual std::vector<char> createShaderBytecode(const std::string& source, const std::string& target, const std::string& entrypoint);
virtual shared_ptr<VertexShader> createVertexShader(const std::vector<char>& bytecode);
virtual shared_ptr<FragmentShader> createFragmentShader(const std::vector<char>& bytecode);
virtual shared_ptr<ShaderProgram> createShaderProgram(
const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader);
virtual shared_ptr<ShaderProgram> createShaderProgramFFP();
virtual shared_ptr<VertexBuffer> createVertexBuffer(size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage);
virtual shared_ptr<IndexBuffer> createIndexBuffer(size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage);
virtual shared_ptr<VertexLayout> createVertexLayout(const std::vector<VertexLayout::Element>& elements);
virtual shared_ptr<Texture> createTexture(Texture::Type type, Texture::Format format, unsigned int width, unsigned int height, unsigned int depth,
unsigned int mipLevels, Texture::Usage usage);
virtual shared_ptr<Renderbuffer> createRenderbuffer(Texture::Format format, unsigned int width, unsigned int height, unsigned int samples);
virtual shared_ptr<Geometry> createGeometryImpl(const shared_ptr<VertexLayout>& layout,
const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers, const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex);
virtual shared_ptr<Framebuffer> createFramebufferImpl(const std::vector<shared_ptr<Renderbuffer>>& color, const shared_ptr<Renderbuffer>& depth);
virtual const DeviceCaps& getCaps() const
{
return caps;
}
virtual DeviceStats getStatistics() const;
DeviceContextGL* getImmediateContextGL()
{
return immediateContext.get();
}
const DeviceCapsGL& getCapsGL() const
{
return caps;
}
const std::vector<ShaderGlobalConstant>& getGlobalConstants() const
{
return globalConstants;
}
private:
DeviceCapsGL caps;
scoped_ptr<DeviceContextGL> immediateContext;
scoped_ptr<FramebufferGL> mainFramebuffer;
std::vector<ShaderGlobalConstant> globalConstants;
unsigned int frameTimeQueryId;
bool frameTimeQueryIssued;
GLsync frameEventQueryId;
bool frameEventQueryIssued;
float gpuTime;
scoped_ptr<ContextGL> glContext;
};
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,163 @@
#include "FramebufferGL.hpp"
#include "TextureGL.hpp"
#include "DeviceGL.hpp"
#include "HeadersGL.hpp"
namespace Aya
{
namespace Graphics
{
RenderbufferGL::RenderbufferGL(Device* device, const shared_ptr<TextureGL>& container, unsigned int target, unsigned int samples)
: Renderbuffer(device, container->getFormat(), container->getWidth(), container->getHeight(), samples)
, owner(container)
, target(target)
, bufferId(0)
{
}
RenderbufferGL::RenderbufferGL(Device* device, Texture::Format format, unsigned int width, unsigned int height, unsigned int samples)
: Renderbuffer(device, format, width, height, samples)
, target(-1)
, bufferId(0)
{
AYAASSERT(samples);
if (samples > device->getCaps().maxSamples)
throw Aya::runtime_error("Unsupported renderbuffer: too many samples (%d)", samples);
glGenRenderbuffers(1, &bufferId);
glBindRenderbuffer(GL_RENDERBUFFER, bufferId);
if (samples > 1)
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, TextureGL::getInternalFormat(format), width, height);
else
glRenderbufferStorage(GL_RENDERBUFFER, TextureGL::getInternalFormat(format), width, height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
}
RenderbufferGL::~RenderbufferGL()
{
if (bufferId)
glDeleteRenderbuffers(1, &bufferId);
}
FramebufferGL::FramebufferGL(Device* device, unsigned int id)
: Framebuffer(device, 0, 0, 1)
, id(id)
{
}
FramebufferGL::FramebufferGL(Device* device, const std::vector<shared_ptr<Renderbuffer>>& color, const shared_ptr<Renderbuffer>& depth)
: Framebuffer(device, 0, 0, 0)
, id(0)
, color(color)
, depth(depth)
{
AYAASSERT(!color.empty());
if (color.size() > device->getCaps().maxDrawBuffers)
throw Aya::runtime_error("Unsupported framebuffer configuration: too many buffers (%d)", (int)color.size());
glGenFramebuffers(1, &id);
glBindFramebuffer(GL_FRAMEBUFFER, id);
for (size_t i = 0; i < color.size(); ++i)
{
RenderbufferGL* buffer = static_cast<RenderbufferGL*>(color[i].get());
AYAASSERT(buffer);
AYAASSERT(!Texture::isFormatDepth(buffer->getFormat()));
if (buffer->getTextureId())
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, buffer->getTarget(), buffer->getTextureId(), 0);
else
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_RENDERBUFFER, buffer->getBufferId());
if (i == 0)
{
width = buffer->getWidth();
height = buffer->getHeight();
samples = buffer->getSamples();
}
else
{
AYAASSERT(width == buffer->getWidth());
AYAASSERT(height == buffer->getHeight());
AYAASSERT(samples == buffer->getSamples());
}
}
if (depth)
{
RenderbufferGL* buffer = static_cast<RenderbufferGL*>(depth.get());
AYAASSERT(buffer->getTextureId() == 0);
AYAASSERT(Texture::isFormatDepth(buffer->getFormat()));
AYAASSERT(width == buffer->getWidth());
AYAASSERT(height == buffer->getHeight());
AYAASSERT(samples == buffer->getSamples());
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, buffer->getBufferId());
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, buffer->getBufferId());
}
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
glDeleteFramebuffers(1, &id);
throw Aya::runtime_error("Unsupported framebuffer configuration: error %x", status);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
FramebufferGL::~FramebufferGL()
{
if (id)
glDeleteFramebuffers(1, &id);
}
void FramebufferGL::download(void* data, unsigned int size)
{
AYAASSERT(size == width * height * 4);
GLint oldfb = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldfb);
glBindFramebuffer(GL_FRAMEBUFFER, id);
#ifndef GLES
glReadBuffer(id == 0 ? GL_BACK : GL_COLOR_ATTACHMENT0);
#endif
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data);
glBindFramebuffer(GL_FRAMEBUFFER, oldfb);
std::vector<char> tempRow(width * 4);
for (unsigned int y = 0; y < height / 2; ++y)
{
char* dataRow = static_cast<char*>(data) + y * width * 4;
char* dataOppositeRow = static_cast<char*>(data) + (height - 1 - y) * width * 4;
memcpy(&tempRow[0], dataRow, width * 4);
memcpy(dataRow, dataOppositeRow, width * 4);
memcpy(dataOppositeRow, &tempRow[0], width * 4);
}
}
void FramebufferGL::updateDimensions(unsigned int width, unsigned int height)
{
AYAASSERT(color.empty() && !depth);
this->width = width;
this->height = height;
}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,67 @@
#pragma once
#include "Core/Framebuffer.hpp"
#include "TextureGL.hpp"
#include <vector>
namespace Aya
{
namespace Graphics
{
class RenderbufferGL : public Renderbuffer
{
public:
RenderbufferGL(Device* device, const shared_ptr<TextureGL>& owner, unsigned int target, unsigned int samples = 1);
RenderbufferGL(Device* device, Texture::Format format, unsigned int width, unsigned int height, unsigned int samples);
~RenderbufferGL();
unsigned int getTextureId() const
{
return owner ? owner->getId() : 0;
}
unsigned int getTarget() const
{
return target;
}
unsigned int getBufferId() const
{
return bufferId;
}
private:
unsigned int target; // TEXTURE_2D or TEXTURE_CUBE_MAP_POSITIVE_X, ...
unsigned int bufferId;
shared_ptr<TextureGL> owner;
};
class FramebufferGL : public Framebuffer
{
public:
FramebufferGL(Device* device, unsigned int id);
FramebufferGL(Device* device, const std::vector<shared_ptr<Renderbuffer>>& color, const shared_ptr<Renderbuffer>& depth);
~FramebufferGL();
virtual void download(void* data, unsigned int size);
void updateDimensions(unsigned int width, unsigned int height);
unsigned int getId() const
{
return id;
}
unsigned int getDrawBuffers() const
{
return color.size();
}
private:
unsigned int id;
std::vector<shared_ptr<Renderbuffer>> color;
shared_ptr<Renderbuffer> depth;
};
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,339 @@
#include "DataModel/GameBasicSettings.hpp"
#include "GeometryGL.hpp"
#include "DeviceGL.hpp"
#include "HeadersGL.hpp"
DYNAMIC_FASTFLAGVARIABLE(FreakyModeEnabled, false)
namespace Aya
{
namespace Graphics
{
static const GLenum gBufferUsageGL[GeometryBuffer::Usage_Count] = {GL_STATIC_DRAW, GL_DYNAMIC_DRAW};
static const GLenum gBufferLockGL[GeometryBuffer::Lock_Count] = {GL_MAP_WRITE_BIT, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT};
struct VertexFormatGL
{
int size;
GLenum type;
bool normalized;
};
static const VertexFormatGL gVertexFormatGL[VertexLayout::Format_Count] = {
{1, GL_FLOAT, false},
{2, GL_FLOAT, false},
{3, GL_FLOAT, false},
{4, GL_FLOAT, false},
{2, GL_SHORT, false},
{4, GL_SHORT, false},
{4, GL_UNSIGNED_BYTE, false},
{4, GL_UNSIGNED_BYTE, true},
};
static const GLenum gGeometryPrimitiveGL[Geometry::Primitive_Count] = {GL_TRIANGLES, GL_LINES, GL_POINTS, GL_TRIANGLE_STRIP};
static unsigned int getVertexAttributeGL(VertexLayout::Semantic semantic, unsigned int index)
{
switch (semantic)
{
case VertexLayout::Semantic_Position:
AYAASSERT(index == 0);
return 0;
case VertexLayout::Semantic_Normal:
AYAASSERT(index == 0);
return 1;
case VertexLayout::Semantic_Color:
AYAASSERT(index < 2);
return 2 + index;
case VertexLayout::Semantic_Texture:
AYAASSERT(index < 8);
return 4 + index;
default:
AYAASSERT(false);
return -1;
}
}
const char* VertexLayoutGL::getShaderAttributeName(unsigned int id)
{
static const char* table[] = {
"vertex",
"normal",
"colour",
"secondary_colour",
"uv0",
"uv1",
"uv2",
"uv3",
"uv4",
"uv5",
"uv6",
"uv7",
};
return (id < ARRAYSIZE(table)) ? table[id] : NULL;
}
VertexLayoutGL::VertexLayoutGL(Device* device, const std::vector<Element>& elements)
: VertexLayout(device, elements)
{
}
VertexLayoutGL::~VertexLayoutGL() {}
template<typename Base>
GeometryBufferGL<Base>::GeometryBufferGL(Device* device, size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage, unsigned int target)
: Base(device, elementSize, elementCount, usage)
, target(target)
, id(0)
, locked(0)
{
}
template<typename Base>
void GeometryBufferGL<Base>::create()
{
glGenBuffers(1, &id);
AYAASSERT(id);
glBindBuffer(target, id);
glBufferData(target, this->elementSize * this->elementCount, NULL, gBufferUsageGL[this->usage]);
}
template<typename Base>
GeometryBufferGL<Base>::~GeometryBufferGL()
{
AYAASSERT(!locked);
glDeleteBuffers(1, &id);
}
template<typename Base>
void* GeometryBufferGL<Base>::lock(GeometryBuffer::LockMode mode)
{
AYAASSERT(!locked);
unsigned int size = this->elementSize * this->elementCount;
const DeviceCapsGL& caps = static_cast<DeviceGL*>(this->device)->getCapsGL();
if (caps.extMapBuffer)
{
glBindBuffer(target, id);
if (caps.extMapBufferRange)
{
locked = glMapBufferRange(target, 0, size, gBufferLockGL[mode]);
}
else
{
if (mode == GeometryBuffer::Lock_Discard)
glBufferData(target, size, NULL, gBufferUsageGL[this->usage]);
locked = glMapBuffer(target, GL_WRITE_ONLY);
}
}
else
{
locked = new char[size];
}
AYAASSERT(locked);
return locked;
}
template<typename Base>
void GeometryBufferGL<Base>::unlock()
{
AYAASSERT(locked);
const DeviceCapsGL& caps = static_cast<DeviceGL*>(this->device)->getCapsGL();
if (caps.extMapBuffer)
{
glBindBuffer(target, id);
glUnmapBuffer(target);
}
else
{
glBindBuffer(target, id);
glBufferSubData(target, 0, this->elementSize * this->elementCount, locked);
delete[] static_cast<char*>(locked);
}
locked = NULL;
}
template<typename Base>
void GeometryBufferGL<Base>::upload(unsigned int offset, const void* data, unsigned int size)
{
AYAASSERT(!locked);
AYAASSERT(offset + size <= this->elementSize * this->elementCount);
glBindBuffer(target, id);
glBufferSubData(target, offset, size, data);
}
VertexBufferGL::VertexBufferGL(Device* device, size_t elementSize, size_t elementCount, Usage usage)
: GeometryBufferGL<VertexBuffer>(device, elementSize, elementCount, usage, GL_ARRAY_BUFFER)
{
create();
}
VertexBufferGL::~VertexBufferGL() {}
IndexBufferGL::IndexBufferGL(Device* device, size_t elementSize, size_t elementCount, Usage usage)
: GeometryBufferGL<IndexBuffer>(device, elementSize, elementCount, usage, GL_ELEMENT_ARRAY_BUFFER)
{
const DeviceCapsGL& caps = static_cast<DeviceGL*>(device)->getCapsGL();
if (elementSize != 2 && elementSize != 4)
throw Aya::runtime_error("Invalid element size: %d", (int)elementSize);
if (elementSize == 4 && !caps.supportsIndex32)
throw Aya::runtime_error("Unsupported element size: %d", (int)elementSize);
create();
}
IndexBufferGL::~IndexBufferGL() {}
GeometryGL::GeometryGL(Device* device, const shared_ptr<VertexLayout>& layout, const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers,
const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex)
: Geometry(device, layout, vertexBuffers, indexBuffer, baseVertexIndex)
, id(0)
, indexElementSize(0)
{
const DeviceCapsGL& caps = static_cast<DeviceGL*>(device)->getCapsGL();
if (caps.extVertexArrayObject)
{
glGenVertexArrays(1, &id);
AYAASSERT(id);
glBindVertexArray(id);
bindArrays();
glBindVertexArray(0);
}
if (indexBuffer)
{
// Cache indexElementSize to reduce cache misses in case we're using VAO
indexElementSize = indexBuffer->getElementSize();
}
}
GeometryGL::~GeometryGL()
{
if (id)
{
glDeleteVertexArrays(1, &id);
}
}
void GeometryGL::draw(Primitive primitive, unsigned int offset, unsigned int count)
{
unsigned int mask = 0;
// Setup vertex/index buffers
if (id)
glBindVertexArray(id);
else
mask = bindArrays();
// Perform draw call
if (indexBuffer)
{
if (mask == 0)
{
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, static_cast<IndexBufferGL*>(indexBuffer.get())->getId());
}
GLenum indexElementType = indexElementSize == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
glDrawElements(Aya::GameBasicSettings::singleton().getWireframeRendering() ? GL_LINES : gGeometryPrimitiveGL[primitive], count,
indexElementType, reinterpret_cast<void*>(offset * indexElementSize));
}
else
glDrawArrays(gGeometryPrimitiveGL[primitive], offset, count);
if (id)
glBindVertexArray(0);
else
unbindArrays(mask);
}
unsigned int GeometryGL::bindArrays()
{
unsigned int mask = 0;
unsigned int vbId = 0;
const std::vector<VertexLayout::Element>& elements = layout->getElements();
for (size_t i = 0; i < elements.size(); ++i)
{
const VertexLayout::Element& e = elements[i];
int index = getVertexAttributeGL(e.semantic, e.semanticIndex);
AYAASSERT(index >= 0);
AYAASSERT(e.stream < vertexBuffers.size());
VertexBufferGL* vb = static_cast<VertexBufferGL*>(vertexBuffers[e.stream].get());
AYAASSERT(e.offset < vb->getElementSize());
size_t stride = vb->getElementSize();
size_t offset = e.offset + stride * baseVertexIndex;
const VertexFormatGL& formatGl = gVertexFormatGL[e.format];
if (vbId != vb->getId())
{
vbId = vb->getId();
glBindBuffer(GL_ARRAY_BUFFER, vbId);
}
glEnableVertexAttribArray(index);
glVertexAttribPointer(index, formatGl.size, formatGl.type, formatGl.normalized, stride, reinterpret_cast<void*>(offset));
mask |= 1 << index;
}
if (indexBuffer)
{
IndexBufferGL* ib = static_cast<IndexBufferGL*>(indexBuffer.get());
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->getId());
}
return mask;
}
void GeometryGL::unbindArrays(unsigned int mask)
{
for (int i = 0; i < 16; ++i)
if (mask & (1 << i))
glDisableVertexAttribArray(i);
}
// Instantiate GeometryBufferGL template
template class GeometryBufferGL<VertexBuffer>;
template class GeometryBufferGL<IndexBuffer>;
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,83 @@
#pragma once
#include "Core/Geometry.hpp"
namespace Aya
{
namespace Graphics
{
class VertexLayoutGL : public VertexLayout
{
public:
static const char* getShaderAttributeName(unsigned int id);
VertexLayoutGL(Device* device, const std::vector<Element>& elements);
~VertexLayoutGL();
};
template<typename Base>
class GeometryBufferGL : public Base
{
public:
GeometryBufferGL(Device* device, size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage, unsigned int target);
~GeometryBufferGL();
virtual void* lock(GeometryBuffer::LockMode mode);
virtual void unlock();
virtual void upload(unsigned int offset, const void* data, unsigned int size);
unsigned int getId() const
{
return id;
}
protected:
void create();
private:
unsigned int target;
unsigned int id;
void* locked;
};
class VertexBufferGL : public GeometryBufferGL<VertexBuffer>
{
public:
VertexBufferGL(Device* device, size_t elementSize, size_t elementCount, Usage usage);
~VertexBufferGL();
};
class IndexBufferGL : public GeometryBufferGL<IndexBuffer>
{
public:
IndexBufferGL(Device* device, size_t elementSize, size_t elementCount, Usage usage);
~IndexBufferGL();
};
class GeometryGL : public Geometry
{
public:
GeometryGL(Device* device, const shared_ptr<VertexLayout>& layout, const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers,
const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex);
~GeometryGL();
void draw(Primitive primitive, unsigned int offset, unsigned int count);
unsigned int getId() const
{
return id;
}
private:
unsigned int id;
unsigned int indexElementSize;
unsigned int bindArrays();
void unbindArrays(unsigned int mask);
};
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,137 @@
#pragma once
#ifdef RBX_PLATFORM_IOS
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#ifdef __OBJC__
#include <OpenGLES/EAGL.h>
#endif
#define GLES
#elif defined(__ANDROID__)
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#define GLES
#else
#include <glad/gl.h>
#endif
#ifdef __ANDROID__
typedef struct __GLsync* GLsync;
typedef uint64_t GLuint64;
#endif
#ifdef GLES
// Core GLES
#define glClearDepth glClearDepthf
// OES_vertex_array_object
GLvoid glBindVertexArray(GLuint array);
GLvoid glDeleteVertexArrays(GLsizei n, const GLuint* arrays);
GLvoid glGenVertexArrays(GLsizei n, GLuint* arrays);
// OES_mapbuffer
#define GL_WRITE_ONLY 0x88B9
GLvoid* glMapBuffer(GLenum target, GLenum access);
GLboolean glUnmapBuffer(GLenum target);
// EXT_map_buffer_range
GLvoid* glMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
#define GL_MAP_READ_BIT 0x0001
#define GL_MAP_WRITE_BIT 0x0002
#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004
#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008
#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010
#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020
// EXT_texture_storage
GLvoid glTexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height);
#define GL_LUMINANCE8 0x8040
#define GL_LUMINANCE8_ALPHA8 0x8045
// GL_OES_packed_depth_stencil
#define GL_DEPTH_STENCIL 0x84F9
#define GL_UNSIGNED_INT_24_8 0x84FA
#define GL_DEPTH24_STENCIL8 0x88F0
// OES_rgb8_rgba8
#define GL_RGBA8 0x8058
// OES_texture_half_float
#define GL_HALF_FLOAT 0x8D61
// EXT_color_buffer_half_float
#define GL_RGBA16F 0x881A
// OES_texture_3D
#define GL_TEXTURE_3D 0x806F
GLvoid glTexImage3D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format,
GLenum type, const GLvoid* pixels);
GLvoid glTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
GLenum format, GLenum type, const GLvoid* pixels);
GLvoid glTexStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth);
// GLES 3.0 formats
#define GL_RED 0x1903
#define GL_RG 0x8227
#define GL_R8 0x8229
#define GL_RG8 0x822B
#define GL_RG16 0x822C
// GLES 3.0 buffer objects
#define GL_DYNAMIC_COPY 0x88EA
#define GL_PIXEL_UNPACK_BUFFER 0x88EC
// GLES 3.0 sync objects
#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117
#define GL_ALREADY_SIGNALED 0x911A
#define GL_TIMEOUT_EXPIRED 0x911B
#define GL_CONDITION_SATISFIED 0x911C
#define GL_WAIT_FAILED 0x911D
#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001
GLsync glFenceSync(GLenum condition, GLbitfield flags);
void glDeleteSync(GLsync sync);
GLenum glClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout);
void glWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout);
// GLES 3.0 MSAA
#define GL_READ_FRAMEBUFFER 0x8CA8
#define GL_DRAW_FRAMEBUFFER 0x8CA9
#define GL_MAX_SAMPLES 0x8D57
GLvoid glBlitFramebuffer(
GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
GLvoid glRenderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
// GLES 3.0 invalidate FBO
GLvoid glInvalidateFramebuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments);
#endif
// EXT_texture_compression_s3tc
#ifndef GL_EXT_texture_compression_s3tc
#define GL_EXT_texture_compression_s3tc 1
#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0
#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1
#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2
#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
#endif
// IMG_texture_compression_pvrtc
#ifndef GL_IMG_texture_compression_pvrtc
#define GL_IMG_texture_compression_pvrtc 1
#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00
#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01
#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02
#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03
#endif
#ifndef GL_ETC1_RGB8_OES
#define GL_ETC1_RGB8_OES (0x8D64)
#endif
// APPLE_texture_max_level, GLES 3.0
#ifndef GL_TEXTURE_MAX_LEVEL
#define GL_TEXTURE_MAX_LEVEL 0x813D
#endif

View File

@@ -0,0 +1,563 @@
#include "ShaderGL.hpp"
#include "GeometryGL.hpp"
#include "DeviceGL.hpp"
#include "HeadersGL.hpp"
#include <set>
#include <map>
LOGGROUP(Graphics)
namespace Aya
{
namespace Graphics
{
static int getShaderParameter(GLuint shader, GLenum pname)
{
GLint result;
glGetShaderiv(shader, pname, &result);
return result;
}
static int getProgramParameter(GLuint program, GLenum pname)
{
GLint result;
glGetProgramiv(program, pname, &result);
return result;
}
template<typename GetParameter, typename GetLog>
static std::string getInfoLog(unsigned int id, GetParameter getParameter, GetLog getLog)
{
int infoLength = getParameter(id, GL_INFO_LOG_LENGTH);
if (infoLength > 0)
{
std::vector<char> buffer(infoLength);
getLog(id, infoLength, NULL, &buffer[0]);
return std::string(&buffer[0]);
}
else
{
return "";
}
}
template<typename GetParameter, typename GetLog>
static void dumpInfoLog(unsigned int id, GetParameter getParameter, GetLog getLog)
{
std::string log = getInfoLog(id, getParameter, getLog);
// Intel and AMD drivers like to say "No errors" for every shader.
if (strstr(log.c_str(), "warn") || strstr(log.c_str(), "Warn") || strstr(log.c_str(), "WARN"))
{
FASTLOG2(FLog::Graphics, "Shader %d has a non-empty infolog (length %d)", id, log.length());
ShaderProgram::dumpToFLog(log, FLog::Graphics);
}
}
static unsigned int compileShader(const std::string& source, GLenum type)
{
unsigned int id = glCreateShader(type);
AYAASSERT(id);
int length = source.length();
const char* data = source.c_str();
glShaderSource(id, 1, &data, &length);
glCompileShader(id);
if (getShaderParameter(id, GL_COMPILE_STATUS) != 1)
{
std::string infoLog = getInfoLog(id, getShaderParameter, glGetShaderInfoLog);
glDeleteShader(id);
throw std::runtime_error(infoLog);
}
dumpInfoLog(id, getShaderParameter, glGetShaderInfoLog);
return id;
}
static unsigned int parseAttribs(const std::string& source, const char* prefix)
{
std::set<std::string> attribs;
size_t offset = 0;
while (offset < source.length())
{
std::string::size_type start = source.find(prefix, offset);
if (start == std::string::npos)
break;
std::string::size_type eol = source.find('\n', start);
if (eol == std::string::npos)
break;
offset = eol;
std::string::size_type semi = source.find(';', start);
std::string::size_type space = source.find_last_of(' ', eol);
if ((start == 0 || source[start - 1] == '\n') && semi != std::string::npos && space != std::string::npos && space < semi)
attribs.insert(source.substr(space + 1, semi - space - 1));
}
unsigned int result = 0;
for (unsigned int attr = 0; attr < 16; ++attr)
{
const char* name = VertexLayoutGL::getShaderAttributeName(attr);
if (name && attribs.count(name))
result |= 1 << attr;
}
return result;
}
static void applyAttribs(unsigned int id, unsigned int attribMask)
{
for (unsigned int attr = 0; attr < 16; ++attr)
if (attribMask & (1 << attr))
{
const char* name = VertexLayoutGL::getShaderAttributeName(attr);
AYAASSERT(name);
glBindAttribLocation(id, attr, name);
}
}
static std::vector<FragmentShaderGL::Sampler> parseSamplers(const std::string& source)
{
std::vector<FragmentShaderGL::Sampler> result;
size_t offset = 0;
while (offset < source.length())
{
std::string::size_type start = source.find("//$$", offset);
if (start == std::string::npos)
return result;
std::string::size_type equals = source.find('=', start);
if (equals == std::string::npos)
return result;
std::string::size_type end = source.find('\n', equals);
if (end == std::string::npos)
return result;
std::string name(source.begin() + start + 4, source.begin() + equals);
std::string value(source.begin() + equals + 1, source.begin() + end);
if (!value.empty() && value[0] == 's')
{
FragmentShaderGL::Sampler entry = {name, atoi(value.c_str() + 1)};
result.push_back(entry);
}
offset = end;
}
return result;
}
static int getSamplerLocation(unsigned int id, const std::string& name)
{
int location;
if ((location = glGetUniformLocation(id, name.c_str())) >= 0)
return location;
if ((location = glGetUniformLocation(id, ("xlu_" + name).c_str())) >= 0)
return location;
return -1;
}
static unsigned int applySamplers(unsigned int id, const std::vector<FragmentShaderGL::Sampler>& samplers)
{
unsigned int mask = 0;
glUseProgram(id);
for (size_t i = 0; i < samplers.size(); ++i)
{
int location = getSamplerLocation(id, samplers[i].name);
if (location >= 0)
{
glUniform1i(location, samplers[i].slot);
mask |= 1 << samplers[i].slot;
}
}
glUseProgram(0);
return mask;
}
static ShaderProgramGL::Uniform::Type getUniformType(GLenum type)
{
switch (type)
{
case GL_FLOAT:
return ShaderProgramGL::Uniform::Type_Float;
case GL_FLOAT_VEC2:
return ShaderProgramGL::Uniform::Type_Float2;
case GL_FLOAT_VEC3:
return ShaderProgramGL::Uniform::Type_Float3;
case GL_FLOAT_VEC4:
return ShaderProgramGL::Uniform::Type_Float4;
case GL_FLOAT_MAT4:
return ShaderProgramGL::Uniform::Type_Float4x4;
default:
return ShaderProgramGL::Uniform::Type_Unknown;
}
}
typedef std::map<std::string, ShaderProgramGL::Uniform> UniformTable;
static UniformTable extractUniforms(unsigned int id)
{
int uniformCount = getProgramParameter(id, GL_ACTIVE_UNIFORMS);
int uniformMaxLength = getProgramParameter(id, GL_ACTIVE_UNIFORM_MAX_LENGTH);
std::vector<char> uniformName(uniformMaxLength + 1);
UniformTable result;
for (int i = 0; i < uniformCount; ++i)
{
GLint size;
GLenum type;
glGetActiveUniform(id, i, uniformMaxLength, NULL, &size, &type, &uniformName[0]);
ShaderProgramGL::Uniform::Type uniformType = getUniformType(type);
int location = glGetUniformLocation(id, &uniformName[0]);
if (location >= 0 && uniformType != ShaderProgramGL::Uniform::Type_Unknown)
{
ShaderProgramGL::Uniform uniform = {location, uniformType, (unsigned int)size, 0};
std::string name(&uniformName[0]);
// Remove xlu_ prefix for entrypoint-local uniforms
if (name.compare(0, 4, "xlu_") == 0)
name.erase(name.begin(), name.begin() + 4);
// Remove [0] from arrays
std::string::size_type index = name.find('[');
if (index != std::string::npos)
name.erase(name.begin() + index, name.end());
result[name] = uniform;
}
}
return result;
}
static const ShaderProgramGL::Uniform* findUniform(const UniformTable& ctab, const std::string& name)
{
UniformTable::const_iterator it = ctab.find(name);
return (it == ctab.end()) ? NULL : &it->second;
}
static inline void transposeMatrix(float* dest, const float* source, const float* lastColumn)
{
dest[0] = source[0];
dest[1] = source[4];
dest[2] = source[8];
dest[3] = lastColumn[0];
dest[4] = source[1];
dest[5] = source[5];
dest[6] = source[9];
dest[7] = lastColumn[1];
dest[8] = source[2];
dest[9] = source[6];
dest[10] = source[10];
dest[11] = lastColumn[2];
dest[12] = source[3];
dest[13] = source[7];
dest[14] = source[11];
dest[15] = lastColumn[3];
}
VertexShaderGL::VertexShaderGL(Device* device, const std::string& source)
: VertexShader(device)
, id(0)
, attribMask(0)
{
id = compileShader(source, GL_VERTEX_SHADER);
attribMask = parseAttribs(source, static_cast<DeviceGL*>(device)->getCapsGL().ext3 ? "in " : "attribute ");
}
VertexShaderGL::~VertexShaderGL()
{
glDeleteShader(id);
}
void VertexShaderGL::reloadBytecode(const std::vector<char>& bytecode)
{
throw std::runtime_error("Bytecode reloading is not supported");
}
FragmentShaderGL::FragmentShaderGL(Device* device, const std::string& source)
: FragmentShader(device)
, id(0)
{
id = compileShader(source, GL_FRAGMENT_SHADER);
samplers = parseSamplers(source);
}
FragmentShaderGL::~FragmentShaderGL()
{
glDeleteShader(id);
}
void FragmentShaderGL::reloadBytecode(const std::vector<char>& bytecode)
{
throw std::runtime_error("Bytecode reloading is not supported");
}
ShaderProgramGL::ShaderProgramGL(Device* device, const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader)
: ShaderProgram(device, vertexShader, fragmentShader)
, id(0)
, uniformWorldMatrix(-1)
, uniformWorldMatrixArray(-1)
, cachedGlobalVersion(0)
, maxWorldTransforms(0)
, samplerMask(0)
{
id = glCreateProgram();
AYAASSERT(id);
glAttachShader(id, static_cast<VertexShaderGL*>(vertexShader.get())->getId());
glAttachShader(id, static_cast<FragmentShaderGL*>(fragmentShader.get())->getId());
// Setup attribute locations
applyAttribs(id, static_cast<VertexShaderGL*>(vertexShader.get())->getAttribMask());
glLinkProgram(id);
if (getProgramParameter(id, GL_LINK_STATUS) != 1)
{
std::string infoLog = getInfoLog(id, getProgramParameter, glGetProgramInfoLog);
glDeleteProgram(id);
throw std::runtime_error(infoLog);
}
dumpInfoLog(id, getProgramParameter, glGetProgramInfoLog);
// Make sure the program uses correct sampler slot assignments
// Note: this resets current program to 0!
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedProgram();
samplerMask = applySamplers(id, static_cast<FragmentShaderGL*>(fragmentShader.get())->getSamplers());
// Populate constant tables
const std::vector<ShaderGlobalConstant>& globalConstants = static_cast<DeviceGL*>(device)->getGlobalConstants();
UniformTable ctab = extractUniforms(id);
for (size_t i = 0; i < globalConstants.size(); ++i)
if (const Uniform* uniform = findUniform(ctab, globalConstants[i].name))
{
Uniform u = *uniform;
u.offset = globalConstants[i].offset;
globalUniforms.push_back(u);
ctab.erase(globalConstants[i].name);
}
// Populate world matrix information
if (const Uniform* uniform = findUniform(ctab, "WorldMatrix"))
{
AYAASSERT(uniform->type == Uniform::Type_Float4x4);
uniformWorldMatrix = uniform->location;
maxWorldTransforms = 1;
ctab.erase("WorldMatrix");
}
if (const Uniform* uniform = findUniform(ctab, "WorldMatrixArray"))
{
AYAASSERT(uniform->type == Uniform::Type_Float4);
AYAASSERT(uniform->size % 3 == 0);
uniformWorldMatrixArray = uniform->location;
maxWorldTransforms = uniform->size / 3;
ctab.erase("WorldMatrixArray");
}
// Populate uniform information
for (UniformTable::iterator it = ctab.begin(); it != ctab.end(); ++it)
{
uniforms.push_back(std::make_pair(it->first, it->second));
}
}
ShaderProgramGL::~ShaderProgramGL()
{
glDeleteProgram(id);
// Shader program destruction invalidates currently bound program id
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedProgram();
}
int ShaderProgramGL::getConstantHandle(const char* name) const
{
for (size_t i = 0; i < uniforms.size(); ++i)
if (uniforms[i].first == name)
return i;
return -1;
}
unsigned int ShaderProgramGL::getMaxWorldTransforms() const
{
return maxWorldTransforms;
}
unsigned int ShaderProgramGL::getSamplerMask() const
{
return samplerMask;
}
void ShaderProgramGL::bind(const void* globalData, unsigned int globalVersion, ShaderProgramGL** cache)
{
if (*cache != this)
{
*cache = this;
glUseProgram(id);
}
if (cachedGlobalVersion != globalVersion)
{
cachedGlobalVersion = globalVersion;
for (size_t i = 0; i < globalUniforms.size(); ++i)
{
const Uniform& u = globalUniforms[i];
const float* data = reinterpret_cast<const float*>(static_cast<const char*>(globalData) + u.offset);
switch (u.type)
{
case Uniform::Type_Float:
glUniform1fv(u.location, 1, data);
break;
case Uniform::Type_Float2:
glUniform2fv(u.location, 1, data);
break;
case Uniform::Type_Float3:
glUniform3fv(u.location, 1, data);
break;
case Uniform::Type_Float4:
glUniform4fv(u.location, 1, data);
break;
case Uniform::Type_Float4x4:
{
float matrix[16];
transposeMatrix(matrix, data, data + 12);
glUniformMatrix4fv(u.location, 1, false, matrix);
break;
}
default:
break;
}
}
}
}
void ShaderProgramGL::setWorldTransforms4x3(const float* data, size_t matrixCount)
{
if (matrixCount == 0)
return;
if (uniformWorldMatrix >= 0)
{
static const float lastColumn[] = {0, 0, 0, 1};
float matrix[16];
transposeMatrix(matrix, data, lastColumn);
glUniformMatrix4fv(uniformWorldMatrix, 1, false, matrix);
}
if (uniformWorldMatrixArray >= 0)
{
AYAASSERT(matrixCount <= maxWorldTransforms);
glUniform4fv(uniformWorldMatrixArray, matrixCount * 3, data);
}
}
void ShaderProgramGL::setConstant(int handle, const float* data, size_t vectorCount)
{
if (handle < 0)
return;
AYAASSERT(static_cast<size_t>(handle) < uniforms.size());
const Uniform& u = uniforms[handle].second;
switch (u.type)
{
case Uniform::Type_Float:
AYAASSERT(vectorCount == 1);
glUniform1fv(u.location, 1, data);
break;
case Uniform::Type_Float2:
AYAASSERT(vectorCount == 1);
glUniform2fv(u.location, 1, data);
break;
case Uniform::Type_Float3:
AYAASSERT(vectorCount == 1);
glUniform3fv(u.location, 1, data);
break;
case Uniform::Type_Float4:
AYAASSERT(vectorCount > 0 && vectorCount <= u.size);
glUniform4fv(u.location, vectorCount, data);
break;
default:
AYAASSERT(false);
}
}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,123 @@
#pragma once
#include "Core/Shader.hpp"
#include <vector>
namespace Aya
{
namespace Graphics
{
class VertexShaderGL : public VertexShader
{
public:
VertexShaderGL(Device* device, const std::string& source);
~VertexShaderGL();
virtual void reloadBytecode(const std::vector<char>& bytecode);
unsigned int getId() const
{
return id;
}
unsigned int getAttribMask() const
{
return attribMask;
}
private:
unsigned int id;
unsigned int attribMask;
};
class FragmentShaderGL : public FragmentShader
{
public:
struct Sampler
{
std::string name;
int slot;
};
FragmentShaderGL(Device* device, const std::string& source);
~FragmentShaderGL();
virtual void reloadBytecode(const std::vector<char>& bytecode);
unsigned int getId() const
{
return id;
}
const std::vector<Sampler>& getSamplers() const
{
return samplers;
}
private:
unsigned int id;
std::vector<Sampler> samplers;
};
class ShaderProgramGL : public ShaderProgram
{
public:
struct Uniform
{
enum Type
{
Type_Unknown,
Type_Float,
Type_Float2,
Type_Float3,
Type_Float4,
Type_Float4x4,
Type_Count
};
int location;
Type type;
unsigned int size;
unsigned int offset;
};
ShaderProgramGL(Device* device, const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader);
~ShaderProgramGL();
virtual int getConstantHandle(const char* name) const;
virtual unsigned int getMaxWorldTransforms() const;
virtual unsigned int getSamplerMask() const;
void bind(const void* globalData, unsigned int globalVersion, ShaderProgramGL** cache);
void setWorldTransforms4x3(const float* data, size_t matrixCount);
void setConstant(int handle, const float* data, size_t vectorCount);
unsigned int getId() const
{
return id;
}
private:
unsigned int id;
int uniformWorldMatrix;
int uniformWorldMatrixArray;
unsigned int cachedGlobalVersion;
std::vector<Uniform> globalUniforms;
std::vector<std::pair<std::string, Uniform>> uniforms;
unsigned int maxWorldTransforms;
unsigned int samplerMask;
};
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,545 @@
#include "TextureGL.hpp"
#include "DeviceGL.hpp"
#include "FramebufferGL.hpp"
#include "HeadersGL.hpp"
LOGGROUP(Graphics)
FASTFLAG(GraphicsTextureCommitChanges)
namespace Aya
{
namespace Graphics
{
static const GLenum gTextureTargetGL[Texture::Type_Count] = {GL_TEXTURE_2D, GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP};
struct TextureFormatGL
{
GLenum internalFormat;
GLenum dataFormat;
GLenum dataType;
};
static const TextureFormatGL gTextureFormatGL[Texture::Format_Count] = {
{GL_R8, GL_RED, GL_UNSIGNED_BYTE},
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE},
{GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1},
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE},
{GL_RG16, GL_RG, GL_UNSIGNED_SHORT},
{GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT},
{GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0, 0},
{GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0, 0},
{GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0, 0},
{GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG, 0, 0},
{GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG, 0, 0},
{GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 0, 0},
{GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, 0, 0},
{GL_ETC1_RGB8_OES, 0, 0},
{GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT},
{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8},
};
const TextureFormatGL& getTextureFormatGL(Texture::Format format, bool ext3)
{
if (!ext3 && format == Texture::Format_L8)
{
static const TextureFormatGL result = {GL_LUMINANCE8_EXT, GL_LUMINANCE, GL_UNSIGNED_BYTE};
return result;
}
if (!ext3 && format == Texture::Format_LA8)
{
static const TextureFormatGL result = {GL_LUMINANCE8_ALPHA8_EXT, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE};
return result;
}
return gTextureFormatGL[format];
}
static const GLenum gSamplerFilterMinGL[SamplerState::Filter_Count][2] = {
{GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST}, {GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR}, {GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR}};
static const GLenum gSamplerFilterMagGL[SamplerState::Filter_Count] = {GL_NEAREST, GL_LINEAR, GL_LINEAR};
static const GLenum gSamplerAddressGL[SamplerState::Address_Count] = {GL_REPEAT, GL_CLAMP_TO_EDGE};
static bool doesFormatSupportPartialUpload(Texture::Format format)
{
return format != Texture::Format_ETC1;
}
static unsigned int createTexture(Texture::Type type, Texture::Format format, unsigned int width, unsigned int height, unsigned int depth,
unsigned int mipLevels, const DeviceCapsGL& caps)
{
GLenum target = gTextureTargetGL[type];
const TextureFormatGL& formatGl = getTextureFormatGL(format, caps.ext3);
unsigned int id = 0;
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &id);
glBindTexture(target, id);
if (caps.extTextureStorage)
{
if (type == Texture::Type_3D)
{
glTexStorage3D(target, mipLevels, formatGl.internalFormat, width, height, depth);
}
else if (type == Texture::Type_2DMultisampled)
{
glTexStorage2DMultisample(target, 4, formatGl.internalFormat, width, height, GL_TRUE);
}
else
{
glTexStorage2D(target, mipLevels, formatGl.internalFormat, width, height);
}
}
else
{
// Is this correct? There's a subtle difference here between desktop GL and GLES
#ifdef GLES
GLenum internalFormat = formatGl.dataFormat;
#else
GLenum internalFormat = formatGl.internalFormat;
#endif
unsigned int faces = (type == Texture::Type_Cube) ? 6 : 1;
for (unsigned int index = 0; index < faces; ++index)
{
GLenum faceTarget = (type == Texture::Type_Cube) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + index : target;
for (unsigned int mip = 0; mip < mipLevels; ++mip)
{
unsigned int mipWidth = Texture::getMipSide(width, mip);
unsigned int mipHeight = Texture::getMipSide(height, mip);
unsigned int mipDepth = Texture::getMipSide(depth, mip);
if (type == Texture::Type_3D)
{
glTexImage3D(faceTarget, mip, internalFormat, mipWidth, mipHeight, mipDepth, 0, formatGl.dataFormat, formatGl.dataType, NULL);
}
else if (Texture::isFormatCompressed(format))
{
unsigned int size = Texture::getImageSize(format, mipWidth, mipHeight);
// glCompressedTexImage does not accept NULL as a valid pointer
std::vector<char> data(size);
glCompressedTexImage2D(faceTarget, mip, formatGl.internalFormat, mipWidth, mipHeight, 0, size, &data[0]);
}
else
{
glTexImage2D(faceTarget, mip, internalFormat, mipWidth, mipHeight, 0, formatGl.dataFormat, formatGl.dataType, NULL);
}
}
}
}
if (caps.supportsTexturePartialMipChain)
glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, mipLevels - 1);
else
AYAASSERT(mipLevels == 1 || mipLevels == Texture::getMaxMipCount(width, height, depth));
glBindTexture(target, 0);
return id;
}
static void uploadTexture(unsigned int id, Texture::Type type, Texture::Format format, unsigned int index, unsigned int mip,
const TextureRegion& region, bool entireRegion, const void* data, const DeviceCapsGL& caps)
{
GLenum target = gTextureTargetGL[type];
GLenum faceTarget = (type == Texture::Type_Cube) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + index : target;
const TextureFormatGL& formatGl = getTextureFormatGL(format, caps.ext3);
glActiveTexture(GL_TEXTURE0);
glBindTexture(target, id);
bool needsCustomAlignment = (!Texture::isFormatCompressed(format) && Texture::getFormatBits(format) < 32);
glPixelStorei(GL_UNPACK_ALIGNMENT, needsCustomAlignment ? 1 : 4);
if (type == Texture::Type_3D)
{
glTexSubImage3D(
faceTarget, mip, region.x, region.y, region.z, region.width, region.height, region.depth, formatGl.dataFormat, formatGl.dataType, data);
}
else
{
if (Texture::isFormatCompressed(format))
{
unsigned int size = Texture::getImageSize(format, region.width, region.height) * region.depth;
if (entireRegion && !doesFormatSupportPartialUpload(format) && !caps.extTextureStorage)
{
glCompressedTexImage2D(faceTarget, mip, formatGl.internalFormat, region.width, region.height, 0, size, data);
}
else
{
glCompressedTexSubImage2D(faceTarget, mip, region.x, region.y, region.width, region.height, formatGl.internalFormat, size, data);
}
}
else if (type == Texture::Type_2DMultisampled)
{
glTexStorage2DMultisample(faceTarget, 4, formatGl.internalFormat, region.width, region.height, GL_TRUE);
}
else
{
glTexSubImage2D(faceTarget, mip, region.x, region.y, region.width, region.height, formatGl.dataFormat, formatGl.dataType, data);
}
}
glBindTexture(target, 0);
}
TextureGL::TextureGL(
Device* device, Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, Usage usage)
: Texture(device, type, format, width, height, depth, mipLevels, usage)
, id(0)
, cachedState(SamplerState::Filter_Count) // initialize state to invalid value to force a cache miss on the next setup
, external(false)
, scratchId(0)
, scratchOffset(0)
, scratchSize(0)
{
const DeviceCapsGL& caps = static_cast<DeviceGL*>(device)->getCapsGL();
const TextureFormatGL& formatGl = getTextureFormatGL(format, caps.ext3);
if (!formatGl.internalFormat)
throw Aya::runtime_error("Unsupported format: %d (platform)", format);
if ((format == Format_RGBA16F) && !caps.supportsTextureHalfFloat)
throw Aya::runtime_error("Unsupported format: %d (caps)", format);
if ((format == Format_BC1 || format == Format_BC2 || format == Format_BC3) && !caps.supportsTextureDXT)
throw Aya::runtime_error("Unsupported format: %d (caps)", format);
if ((format == Format_PVRTC_RGB2 || format == Format_PVRTC_RGBA2 || format == Format_PVRTC_RGB4 || format == Format_PVRTC_RGBA4) &&
!caps.supportsTexturePVR)
throw Aya::runtime_error("Unsupported format: %d (caps)", format);
if (format == Format_ETC1 && !caps.supportsTextureETC1)
throw Aya::runtime_error("Unsupported format: %d (capse)", format);
if (!caps.supportsTextureNPOT && ((width & (width - 1)) || (height & (height - 1)) || (depth & (depth - 1))) &&
(usage != Usage_Renderbuffer || mipLevels > 1))
throw Aya::runtime_error("Unsupported dimensions: %dx%dx%d (NPOT)", width, height, depth);
if (!caps.supportsTexture3D && type == Type_3D)
throw Aya::runtime_error("Unsupported type: 3D");
if (FFlag::GraphicsTextureCommitChanges && usage == Usage_Dynamic && isFormatCompressed(format))
throw Aya::runtime_error("Unsupported format: compressed formats are not supported for dynamic textures");
// createTexture poisons the binding on the first stage
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTextureStage(0);
id = createTexture(type, format, width, height, depth, mipLevels, caps);
if (FFlag::GraphicsTextureCommitChanges && usage == Usage_Dynamic && caps.ext3)
{
scratchSize = getTextureSize(type, format, width, height, depth, mipLevels);
glGenBuffers(1, &scratchId);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratchId);
glBufferData(GL_PIXEL_UNPACK_BUFFER, scratchSize, NULL, GL_DYNAMIC_COPY);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
}
TextureGL::TextureGL(Device* device, Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels,
Usage usage, unsigned int id)
: Texture(device, type, format, width, height, depth, mipLevels, usage)
, cachedState(SamplerState::Filter_Count) // initialize state to invalid value to force a cache miss on the next setup
, id(id)
, external(true)
, scratchId(0)
, scratchOffset(0)
, scratchSize(0)
{
}
TextureGL::~TextureGL()
{
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTexture(this);
if (scratchId)
glDeleteBuffers(1, &scratchId);
if (!external)
glDeleteTextures(1, &id);
}
void TextureGL::upload(unsigned int index, unsigned int mip, const TextureRegion& region, const void* data, unsigned int size)
{
const DeviceCapsGL& caps = static_cast<DeviceGL*>(device)->getCapsGL();
AYAASSERT(index < (type == Type_Cube ? 6u : 1u));
unsigned int mipWidth = getMipSide(width, mip);
unsigned int mipHeight = getMipSide(height, mip);
unsigned int mipDepth = getMipSide(depth, mip);
AYAASSERT(mip < mipLevels);
AYAASSERT(region.x + region.width <= mipWidth);
AYAASSERT(region.y + region.height <= mipHeight);
AYAASSERT(region.z + region.depth <= mipDepth);
AYAASSERT(size == getImageSize(format, region.width, region.height) * region.depth);
if (supportsLocking())
{
unsigned int bpp = getFormatBits(format) / 8;
LockResult lr = lock(index, mip, region);
for (unsigned int z = 0; z < region.depth; ++z)
for (unsigned int y = 0; y < region.height; ++y)
{
unsigned int sourceOffset = (region.width * region.height * z + region.width * y) * bpp;
unsigned int targetOffset = lr.slicePitch * z + lr.rowPitch * y;
memcpy(static_cast<char*>(lr.data) + targetOffset, static_cast<const char*>(data) + sourceOffset, region.width * bpp);
}
unlock(index, mip);
}
else
{
// uploadTexture poisons the binding on the first stage
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTextureStage(0);
bool entireRegion = (region.x == 0 && region.y == 0 && region.width == mipWidth && region.height == mipHeight);
uploadTexture(id, type, format, index, mip, region, entireRegion, data, caps);
}
}
#ifdef GLES
bool TextureGL::download(unsigned int index, unsigned int mip, void* data, unsigned int size)
{
return false;
}
#else
bool TextureGL::download(unsigned int index, unsigned int mip, void* data, unsigned int size)
{
const DeviceCapsGL& caps = static_cast<DeviceGL*>(device)->getCapsGL();
GLenum target = gTextureTargetGL[type];
const TextureFormatGL& formatGl = getTextureFormatGL(format, caps.ext3);
AYAASSERT(index < (type == Type_Cube ? 6u : 1u));
GLenum faceTarget = (type == Type_Cube) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + index : target;
unsigned int mipWidth = getMipSide(width, mip);
unsigned int mipHeight = getMipSide(height, mip);
unsigned int mipDepth = getMipSide(depth, mip);
AYAASSERT(mip < mipLevels);
AYAASSERT(size == getImageSize(format, mipWidth, mipHeight) * mipDepth);
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTextureStage(0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(target, id);
bool needsCustomAlignment = (!isFormatCompressed(format) && getFormatBits(format) < 32);
glPixelStorei(GL_PACK_ALIGNMENT, needsCustomAlignment ? 1 : 4);
if (isFormatCompressed(format))
glGetCompressedTexImage(faceTarget, mip, data);
else
glGetTexImage(faceTarget, mip, formatGl.dataFormat, formatGl.dataType, data);
glBindTexture(target, 0);
return true;
}
#endif
bool TextureGL::supportsLocking() const
{
if (FFlag::GraphicsTextureCommitChanges)
return scratchId != 0;
return false;
}
Texture::LockResult TextureGL::lock(unsigned int index, unsigned int mip, const TextureRegion& region)
{
if (!scratchId)
{
Texture::LockResult result = {0, 0, 0};
return result;
}
AYAASSERT(index < (type == Type_Cube ? 6u : 1u));
unsigned int mipWidth = getMipSide(width, mip);
unsigned int mipHeight = getMipSide(height, mip);
unsigned int mipDepth = getMipSide(depth, mip);
AYAASSERT(mip < mipLevels);
AYAASSERT(region.x + region.width <= mipWidth);
AYAASSERT(region.y + region.height <= mipHeight);
AYAASSERT(region.z + region.depth <= mipDepth);
unsigned int size = getImageSize(format, region.width, region.height) * region.depth;
unsigned int flags = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT;
if (scratchOffset + size > scratchSize)
{
commitChanges();
scratchOffset = 0;
flags = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT;
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratchId);
void* data = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, scratchOffset, size, flags);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
Change change = {index, mip, region, scratchOffset};
scratchOffset += size;
pendingChanges.push_back(change);
Texture::LockResult result = {data, getImageSize(format, region.width, 1), getImageSize(format, region.width, region.height)};
return result;
}
void TextureGL::unlock(unsigned int index, unsigned int mip)
{
if (!scratchId)
return;
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratchId);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
shared_ptr<Renderbuffer> TextureGL::getRenderbuffer(unsigned int index, unsigned int mip)
{
AYAASSERT(usage == Usage_Renderbuffer);
AYAASSERT(mip == 0);
AYAASSERT(index < (type == Type_Cube ? 6u : 1u));
weak_ptr<Renderbuffer>& slot = renderBuffers[std::make_pair(index, mip)];
shared_ptr<Renderbuffer> rb = slot.lock();
if (!rb)
{
GLenum target = (type == Texture::Type_Cube) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + index : gTextureTargetGL[type];
rb.reset(new RenderbufferGL(device, shared_from_this(), target, getType() == Texture::Type_2DMultisampled ? 4 : 1));
slot = rb;
}
return rb;
}
void TextureGL::commitChanges()
{
if (!FFlag::GraphicsTextureCommitChanges)
return;
const DeviceCapsGL& caps = static_cast<DeviceGL*>(device)->getCapsGL();
if (pendingChanges.empty())
return;
// uploadTexture poisons the binding on the first stage
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTextureStage(0);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, scratchId);
for (auto& change : pendingChanges)
{
AYAASSERT(change.scratchOffset < scratchOffset);
bool entireRegion = false; // this is only important for compressed formats that we don't support
uploadTexture(id, type, format, change.index, change.mip, change.region, entireRegion, reinterpret_cast<void*>(change.scratchOffset), caps);
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
pendingChanges.clear();
}
void TextureGL::generateMipmaps()
{
AYAASSERT(usage != Usage_Dynamic);
if (mipLevels <= 1)
return;
static_cast<DeviceGL*>(device)->getImmediateContextGL()->invalidateCachedTextureStage(0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(gTextureTargetGL[type], id);
glGenerateMipmap(gTextureTargetGL[type]);
glBindTexture(gTextureTargetGL[type], 0);
}
void TextureGL::bind(unsigned int stage, const SamplerState& state, TextureGL** cache)
{
if (*cache != this || cachedState != state)
{
glActiveTexture(GL_TEXTURE0 + stage);
GLenum target = gTextureTargetGL[type];
if (*cache != this)
{
*cache = this;
glBindTexture(target, id);
}
if (cachedState != state)
{
cachedState = state;
bool hasMips = mipLevels > 1;
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, gSamplerFilterMinGL[state.getFilter()][hasMips]);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, gSamplerFilterMagGL[state.getFilter()]);
glTexParameteri(target, GL_TEXTURE_WRAP_S, gSamplerAddressGL[state.getAddress()]);
glTexParameteri(target, GL_TEXTURE_WRAP_T, gSamplerAddressGL[state.getAddress()]);
#ifndef GLES
int anisotropy = state.getFilter() == SamplerState::Filter_Anisotropic ? state.getAnisotropy() : 1;
glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, static_cast<float>(anisotropy));
#endif
}
}
}
unsigned int TextureGL::getTarget() const
{
return gTextureTargetGL[type];
}
unsigned int TextureGL::getInternalFormat(Format format)
{
return gTextureFormatGL[format].internalFormat;
}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,75 @@
#pragma once
#include "Core/Texture.hpp"
#include "Core/States.hpp"
#include <boost/enable_shared_from_this.hpp>
#include <map>
#include <vector>
namespace Aya
{
namespace Graphics
{
class TextureGL
: public Texture
, public boost::enable_shared_from_this<TextureGL>
{
public:
TextureGL(
Device* device, Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, Usage usage);
TextureGL(Device* device, Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels,
Usage usage, unsigned int id);
~TextureGL();
virtual void upload(unsigned int index, unsigned int mip, const TextureRegion& region, const void* data, unsigned int size);
virtual bool download(unsigned int index, unsigned int mip, void* data, unsigned int size);
virtual bool supportsLocking() const;
virtual LockResult lock(unsigned int index, unsigned int mip, const TextureRegion& region);
virtual void unlock(unsigned int index, unsigned int mip);
virtual shared_ptr<Renderbuffer> getRenderbuffer(unsigned int index, unsigned int mip);
virtual void commitChanges();
virtual void generateMipmaps();
void bind(unsigned int stage, const SamplerState& state, TextureGL** cache);
unsigned int getId() const
{
return id;
}
unsigned int getTarget() const;
static unsigned int getInternalFormat(Format format);
private:
struct Change
{
unsigned int index;
unsigned int mip;
TextureRegion region;
unsigned int scratchOffset;
};
unsigned int id;
SamplerState cachedState;
bool external;
unsigned int scratchId;
unsigned int scratchOffset;
unsigned int scratchSize;
std::vector<Change> pendingChanges;
typedef std::map<std::pair<unsigned, unsigned>, weak_ptr<Renderbuffer>> RenderBufferMap;
RenderBufferMap renderBuffers;
};
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,175 @@
#ifndef __eglplatform_h_
#define __eglplatform_h_
/*
** Copyright 2007-2020 The Khronos Group Inc.
** SPDX-License-Identifier: Apache-2.0
*/
/* Platform-specific types and definitions for egl.h
*
* Adopters may modify khrplatform.h and this file to suit their platform.
* You are encouraged to submit all modifications to the Khronos group so that
* they can be included in future versions of this file. Please submit changes
* by filing an issue or pull request on the public Khronos EGL Registry, at
* https://www.github.com/KhronosGroup/EGL-Registry/
*/
#include <KHR/khrplatform.h>
/* Macros used in EGL function prototype declarations.
*
* EGL functions should be prototyped as:
*
* EGLAPI return-type EGLAPIENTRY eglFunction(arguments);
* typedef return-type (EXPAPIENTRYP PFNEGLFUNCTIONPROC) (arguments);
*
* KHRONOS_APICALL and KHRONOS_APIENTRY are defined in KHR/khrplatform.h
*/
#ifndef EGLAPI
#define EGLAPI KHRONOS_APICALL
#endif
#ifndef EGLAPIENTRY
#define EGLAPIENTRY KHRONOS_APIENTRY
#endif
#define EGLAPIENTRYP EGLAPIENTRY*
/* The types NativeDisplayType, NativeWindowType, and NativePixmapType
* are aliases of window-system-dependent types, such as X Display * or
* Windows Device Context. They must be defined in platform-specific
* code below. The EGL-prefixed versions of Native*Type are the same
* types, renamed in EGL 1.3 so all types in the API start with "EGL".
*
* Khronos STRONGLY RECOMMENDS that you use the default definitions
* provided below, since these changes affect both binary and source
* portability of applications using EGL running on different EGL
* implementations.
*/
#if defined(EGL_NO_PLATFORM_SPECIFIC_TYPES)
typedef void *EGLNativeDisplayType;
typedef void *EGLNativePixmapType;
typedef void *EGLNativeWindowType;
#elif defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#include <windows.h>
typedef HDC EGLNativeDisplayType;
typedef HBITMAP EGLNativePixmapType;
typedef HWND EGLNativeWindowType;
#elif defined(__QNX__)
typedef khronos_uintptr_t EGLNativeDisplayType;
typedef struct _screen_pixmap* EGLNativePixmapType; /* screen_pixmap_t */
typedef struct _screen_window* EGLNativeWindowType; /* screen_window_t */
#elif defined(__EMSCRIPTEN__)
typedef int EGLNativeDisplayType;
typedef int EGLNativePixmapType;
typedef int EGLNativeWindowType;
#elif defined(__WINSCW__) || defined(__SYMBIAN32__) /* Symbian */
typedef int EGLNativeDisplayType;
typedef void *EGLNativePixmapType;
typedef void *EGLNativeWindowType;
#elif defined(WL_EGL_PLATFORM)
typedef struct wl_display *EGLNativeDisplayType;
typedef struct wl_egl_pixmap *EGLNativePixmapType;
typedef struct wl_egl_window *EGLNativeWindowType;
#elif defined(__GBM__)
typedef struct gbm_device *EGLNativeDisplayType;
typedef struct gbm_bo *EGLNativePixmapType;
typedef void *EGLNativeWindowType;
#elif defined(__ANDROID__) || defined(ANDROID)
struct ANativeWindow;
struct egl_native_pixmap_t;
typedef void* EGLNativeDisplayType;
typedef struct egl_native_pixmap_t* EGLNativePixmapType;
typedef struct ANativeWindow* EGLNativeWindowType;
#elif defined(USE_OZONE)
typedef intptr_t EGLNativeDisplayType;
typedef intptr_t EGLNativePixmapType;
typedef intptr_t EGLNativeWindowType;
#elif defined(USE_X11)
/* X11 (tentative) */
#include <X11/Xlib.h>
#include <X11/Xutil.h>
typedef Display *EGLNativeDisplayType;
typedef Pixmap EGLNativePixmapType;
typedef Window EGLNativeWindowType;
#elif defined(__unix__)
typedef void *EGLNativeDisplayType;
typedef khronos_uintptr_t EGLNativePixmapType;
typedef khronos_uintptr_t EGLNativeWindowType;
#elif defined(__APPLE__)
typedef int EGLNativeDisplayType;
typedef void *EGLNativePixmapType;
typedef void *EGLNativeWindowType;
#elif defined(__HAIKU__)
#include <kernel/image.h>
typedef void *EGLNativeDisplayType;
typedef khronos_uintptr_t EGLNativePixmapType;
typedef khronos_uintptr_t EGLNativeWindowType;
#elif defined(__Fuchsia__)
typedef void *EGLNativeDisplayType;
typedef khronos_uintptr_t EGLNativePixmapType;
typedef khronos_uintptr_t EGLNativeWindowType;
#else
#error "Platform not recognized"
#endif
/* EGL 1.2 types, renamed for consistency in EGL 1.3 */
typedef EGLNativeDisplayType NativeDisplayType;
typedef EGLNativePixmapType NativePixmapType;
typedef EGLNativeWindowType NativeWindowType;
/* Define EGLint. This must be a signed integral type large enough to contain
* all legal attribute names and values passed into and out of EGL, whether
* their type is boolean, bitmask, enumerant (symbolic constant), integer,
* handle, or other. While in general a 32-bit integer will suffice, if
* handles are 64 bit types, then EGLint should be defined as a signed 64-bit
* integer type.
*/
typedef khronos_int32_t EGLint;
/* C++ / C typecast macros for special EGL handle values */
#if defined(__cplusplus)
#define EGL_CAST(type, value) (static_cast<type>(value))
#else
#define EGL_CAST(type, value) ((type) (value))
#endif
#endif /* __eglplatform_h */

View File

@@ -0,0 +1,311 @@
#ifndef __khrplatform_h_
#define __khrplatform_h_
/*
** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
/* Khronos platform-specific types and definitions.
*
* The master copy of khrplatform.h is maintained in the Khronos EGL
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
* The last semantic modification to khrplatform.h was at commit ID:
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file.
* Please submit changes by filing pull requests or issues on
* the EGL Registry repository linked above.
*
*
* See the Implementer's Guidelines for information about where this file
* should be located on your system and for more details of its use:
* http://www.khronos.org/registry/implementers_guide.pdf
*
* This file should be included as
* #include <KHR/khrplatform.h>
* by Khronos client API header files that use its types and defines.
*
* The types in khrplatform.h should only be used to define API-specific types.
*
* Types defined in khrplatform.h:
* khronos_int8_t signed 8 bit
* khronos_uint8_t unsigned 8 bit
* khronos_int16_t signed 16 bit
* khronos_uint16_t unsigned 16 bit
* khronos_int32_t signed 32 bit
* khronos_uint32_t unsigned 32 bit
* khronos_int64_t signed 64 bit
* khronos_uint64_t unsigned 64 bit
* khronos_intptr_t signed same number of bits as a pointer
* khronos_uintptr_t unsigned same number of bits as a pointer
* khronos_ssize_t signed size
* khronos_usize_t unsigned size
* khronos_float_t signed 32 bit floating point
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
* nanoseconds
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
* khronos_boolean_enum_t enumerated boolean type. This should
* only be used as a base type when a client API's boolean type is
* an enum. Client APIs which use an integer or other type for
* booleans cannot use this as the base type for their boolean.
*
* Tokens defined in khrplatform.h:
*
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
*
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
*
* Calling convention macros defined in this file:
* KHRONOS_APICALL
* KHRONOS_APIENTRY
* KHRONOS_APIATTRIBUTES
*
* These may be used in function prototypes as:
*
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
* int arg1,
* int arg2) KHRONOS_APIATTRIBUTES;
*/
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
# define KHRONOS_STATIC 1
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APICALL
*-------------------------------------------------------------------------
* This precedes the return type of the function in the function prototype.
*/
#if defined(KHRONOS_STATIC)
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
* header compatible with static linking. */
# define KHRONOS_APICALL
#elif defined(_WIN32)
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
# define KHRONOS_APICALL __attribute__((visibility("default")))
#else
# define KHRONOS_APICALL
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIENTRY
*-------------------------------------------------------------------------
* This follows the return type of the function and precedes the function
* name in the function prototype.
*/
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
/* Win32 but not WinCE */
# define KHRONOS_APIENTRY __stdcall
#else
# define KHRONOS_APIENTRY
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIATTRIBUTES
*-------------------------------------------------------------------------
* This follows the closing parenthesis of the function prototype arguments.
*/
#if defined (__ARMCC_2__)
#define KHRONOS_APIATTRIBUTES __softfp
#else
#define KHRONOS_APIATTRIBUTES
#endif
/*-------------------------------------------------------------------------
* basic type definitions
*-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
/*
* Using <stdint.h>
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
/*
* To support platform where unsigned long cannot be used interchangeably with
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
* unsigned long long or similar (this results in different C++ name mangling).
* To avoid changes for existing platforms, we restrict usage of intptr_t to
* platforms where the size of a pointer is larger than the size of long.
*/
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
#define KHRONOS_USE_INTPTR_T
#endif
#endif
#elif defined(__VMS ) || defined(__sgi)
/*
* Using <inttypes.h>
*/
#include <inttypes.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
/*
* Win32
*/
typedef __int32 khronos_int32_t;
typedef unsigned __int32 khronos_uint32_t;
typedef __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__sun__) || defined(__digital__)
/*
* Sun or Digital
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#if defined(__arch64__) || defined(_LP64)
typedef long int khronos_int64_t;
typedef unsigned long int khronos_uint64_t;
#else
typedef long long int khronos_int64_t;
typedef unsigned long long int khronos_uint64_t;
#endif /* __arch64__ */
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif 0
/*
* Hypothetical platform with no float or int64 support
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#define KHRONOS_SUPPORT_INT64 0
#define KHRONOS_SUPPORT_FLOAT 0
#else
/*
* Generic fallback
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#endif
/*
* Types that are (so far) the same on all platforms
*/
typedef signed char khronos_int8_t;
typedef unsigned char khronos_uint8_t;
typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
/*
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/
#ifdef KHRONOS_USE_INTPTR_T
typedef intptr_t khronos_intptr_t;
typedef uintptr_t khronos_uintptr_t;
#elif defined(_WIN64)
typedef signed long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
#else
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
#endif
#if defined(_WIN64)
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#endif
#if KHRONOS_SUPPORT_FLOAT
/*
* Float type
*/
typedef float khronos_float_t;
#endif
#if KHRONOS_SUPPORT_INT64
/* Time types
*
* These types can be used to represent a time interval in nanoseconds or
* an absolute Unadjusted System Time. Unadjusted System Time is the number
* of nanoseconds since some arbitrary system event (e.g. since the last
* time the system booted). The Unadjusted System Time is an unsigned
* 64 bit value that wraps back to 0 every 584 years. Time intervals
* may be either signed or unsigned.
*/
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
typedef khronos_int64_t khronos_stime_nanoseconds_t;
#endif
/*
* Dummy value used to pad enum types to 32 bits.
*/
#ifndef KHRONOS_MAX_ENUM
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
#endif
/*
* Enumerated boolean type
*
* Values other than zero should be considered to be true. Therefore
* comparisons should not be made against KHRONOS_TRUE.
*/
typedef enum {
KHRONOS_FALSE = 0,
KHRONOS_TRUE = 1,
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
} khronos_boolean_enum_t;
#endif /* __khrplatform_h_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,798 @@
/**
* SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glad/glx.h>
#ifndef GLAD_IMPL_UTIL_C_
#define GLAD_IMPL_UTIL_C_
#ifdef _MSC_VER
#define GLAD_IMPL_UTIL_SSCANF sscanf_s
#else
#define GLAD_IMPL_UTIL_SSCANF sscanf
#endif
#endif /* GLAD_IMPL_UTIL_C_ */
#ifdef __cplusplus
extern "C" {
#endif
int GLAD_GLX_VERSION_1_0 = 0;
int GLAD_GLX_VERSION_1_1 = 0;
int GLAD_GLX_VERSION_1_2 = 0;
int GLAD_GLX_VERSION_1_3 = 0;
int GLAD_GLX_VERSION_1_4 = 0;
int GLAD_GLX_3DFX_multisample = 0;
int GLAD_GLX_AMD_gpu_association = 0;
int GLAD_GLX_ARB_context_flush_control = 0;
int GLAD_GLX_ARB_create_context = 0;
int GLAD_GLX_ARB_create_context_no_error = 0;
int GLAD_GLX_ARB_create_context_profile = 0;
int GLAD_GLX_ARB_create_context_robustness = 0;
int GLAD_GLX_ARB_fbconfig_float = 0;
int GLAD_GLX_ARB_framebuffer_sRGB = 0;
int GLAD_GLX_ARB_get_proc_address = 0;
int GLAD_GLX_ARB_multisample = 0;
int GLAD_GLX_ARB_robustness_application_isolation = 0;
int GLAD_GLX_ARB_robustness_share_group_isolation = 0;
int GLAD_GLX_ARB_vertex_buffer_object = 0;
int GLAD_GLX_EXT_buffer_age = 0;
int GLAD_GLX_EXT_context_priority = 0;
int GLAD_GLX_EXT_create_context_es2_profile = 0;
int GLAD_GLX_EXT_create_context_es_profile = 0;
int GLAD_GLX_EXT_fbconfig_packed_float = 0;
int GLAD_GLX_EXT_framebuffer_sRGB = 0;
int GLAD_GLX_EXT_get_drawable_type = 0;
int GLAD_GLX_EXT_import_context = 0;
int GLAD_GLX_EXT_libglvnd = 0;
int GLAD_GLX_EXT_no_config_context = 0;
int GLAD_GLX_EXT_stereo_tree = 0;
int GLAD_GLX_EXT_swap_control = 0;
int GLAD_GLX_EXT_swap_control_tear = 0;
int GLAD_GLX_EXT_texture_from_pixmap = 0;
int GLAD_GLX_EXT_visual_info = 0;
int GLAD_GLX_EXT_visual_rating = 0;
int GLAD_GLX_INTEL_swap_event = 0;
int GLAD_GLX_MESA_agp_offset = 0;
int GLAD_GLX_MESA_copy_sub_buffer = 0;
int GLAD_GLX_MESA_pixmap_colormap = 0;
int GLAD_GLX_MESA_query_renderer = 0;
int GLAD_GLX_MESA_release_buffers = 0;
int GLAD_GLX_MESA_set_3dfx_mode = 0;
int GLAD_GLX_MESA_swap_control = 0;
int GLAD_GLX_NV_copy_buffer = 0;
int GLAD_GLX_NV_copy_image = 0;
int GLAD_GLX_NV_delay_before_swap = 0;
int GLAD_GLX_NV_float_buffer = 0;
int GLAD_GLX_NV_multigpu_context = 0;
int GLAD_GLX_NV_multisample_coverage = 0;
int GLAD_GLX_NV_present_video = 0;
int GLAD_GLX_NV_robustness_video_memory_purge = 0;
int GLAD_GLX_NV_swap_group = 0;
int GLAD_GLX_NV_video_capture = 0;
int GLAD_GLX_NV_video_out = 0;
int GLAD_GLX_OML_swap_method = 0;
int GLAD_GLX_OML_sync_control = 0;
int GLAD_GLX_SGIS_blended_overlay = 0;
int GLAD_GLX_SGIS_multisample = 0;
int GLAD_GLX_SGIS_shared_multisample = 0;
int GLAD_GLX_SGIX_fbconfig = 0;
int GLAD_GLX_SGIX_hyperpipe = 0;
int GLAD_GLX_SGIX_pbuffer = 0;
int GLAD_GLX_SGIX_swap_barrier = 0;
int GLAD_GLX_SGIX_swap_group = 0;
int GLAD_GLX_SGIX_video_resize = 0;
int GLAD_GLX_SGIX_visual_select_group = 0;
int GLAD_GLX_SGI_cushion = 0;
int GLAD_GLX_SGI_make_current_read = 0;
int GLAD_GLX_SGI_swap_control = 0;
int GLAD_GLX_SGI_video_sync = 0;
int GLAD_GLX_SUN_get_transparent_index = 0;
PFNGLXBINDCHANNELTOWINDOWSGIXPROC glad_glXBindChannelToWindowSGIX = NULL;
PFNGLXBINDHYPERPIPESGIXPROC glad_glXBindHyperpipeSGIX = NULL;
PFNGLXBINDSWAPBARRIERNVPROC glad_glXBindSwapBarrierNV = NULL;
PFNGLXBINDSWAPBARRIERSGIXPROC glad_glXBindSwapBarrierSGIX = NULL;
PFNGLXBINDTEXIMAGEEXTPROC glad_glXBindTexImageEXT = NULL;
PFNGLXBINDVIDEOCAPTUREDEVICENVPROC glad_glXBindVideoCaptureDeviceNV = NULL;
PFNGLXBINDVIDEODEVICENVPROC glad_glXBindVideoDeviceNV = NULL;
PFNGLXBINDVIDEOIMAGENVPROC glad_glXBindVideoImageNV = NULL;
PFNGLXBLITCONTEXTFRAMEBUFFERAMDPROC glad_glXBlitContextFramebufferAMD = NULL;
PFNGLXCHANNELRECTSGIXPROC glad_glXChannelRectSGIX = NULL;
PFNGLXCHANNELRECTSYNCSGIXPROC glad_glXChannelRectSyncSGIX = NULL;
PFNGLXCHOOSEFBCONFIGPROC glad_glXChooseFBConfig = NULL;
PFNGLXCHOOSEFBCONFIGSGIXPROC glad_glXChooseFBConfigSGIX = NULL;
PFNGLXCHOOSEVISUALPROC glad_glXChooseVisual = NULL;
PFNGLXCOPYBUFFERSUBDATANVPROC glad_glXCopyBufferSubDataNV = NULL;
PFNGLXCOPYCONTEXTPROC glad_glXCopyContext = NULL;
PFNGLXCOPYIMAGESUBDATANVPROC glad_glXCopyImageSubDataNV = NULL;
PFNGLXCOPYSUBBUFFERMESAPROC glad_glXCopySubBufferMESA = NULL;
PFNGLXCREATEASSOCIATEDCONTEXTAMDPROC glad_glXCreateAssociatedContextAMD = NULL;
PFNGLXCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC glad_glXCreateAssociatedContextAttribsAMD = NULL;
PFNGLXCREATECONTEXTPROC glad_glXCreateContext = NULL;
PFNGLXCREATECONTEXTATTRIBSARBPROC glad_glXCreateContextAttribsARB = NULL;
PFNGLXCREATECONTEXTWITHCONFIGSGIXPROC glad_glXCreateContextWithConfigSGIX = NULL;
PFNGLXCREATEGLXPBUFFERSGIXPROC glad_glXCreateGLXPbufferSGIX = NULL;
PFNGLXCREATEGLXPIXMAPPROC glad_glXCreateGLXPixmap = NULL;
PFNGLXCREATEGLXPIXMAPMESAPROC glad_glXCreateGLXPixmapMESA = NULL;
PFNGLXCREATEGLXPIXMAPWITHCONFIGSGIXPROC glad_glXCreateGLXPixmapWithConfigSGIX = NULL;
PFNGLXCREATENEWCONTEXTPROC glad_glXCreateNewContext = NULL;
PFNGLXCREATEPBUFFERPROC glad_glXCreatePbuffer = NULL;
PFNGLXCREATEPIXMAPPROC glad_glXCreatePixmap = NULL;
PFNGLXCREATEWINDOWPROC glad_glXCreateWindow = NULL;
PFNGLXCUSHIONSGIPROC glad_glXCushionSGI = NULL;
PFNGLXDELAYBEFORESWAPNVPROC glad_glXDelayBeforeSwapNV = NULL;
PFNGLXDELETEASSOCIATEDCONTEXTAMDPROC glad_glXDeleteAssociatedContextAMD = NULL;
PFNGLXDESTROYCONTEXTPROC glad_glXDestroyContext = NULL;
PFNGLXDESTROYGLXPBUFFERSGIXPROC glad_glXDestroyGLXPbufferSGIX = NULL;
PFNGLXDESTROYGLXPIXMAPPROC glad_glXDestroyGLXPixmap = NULL;
PFNGLXDESTROYHYPERPIPECONFIGSGIXPROC glad_glXDestroyHyperpipeConfigSGIX = NULL;
PFNGLXDESTROYPBUFFERPROC glad_glXDestroyPbuffer = NULL;
PFNGLXDESTROYPIXMAPPROC glad_glXDestroyPixmap = NULL;
PFNGLXDESTROYWINDOWPROC glad_glXDestroyWindow = NULL;
PFNGLXENUMERATEVIDEOCAPTUREDEVICESNVPROC glad_glXEnumerateVideoCaptureDevicesNV = NULL;
PFNGLXENUMERATEVIDEODEVICESNVPROC glad_glXEnumerateVideoDevicesNV = NULL;
PFNGLXFREECONTEXTEXTPROC glad_glXFreeContextEXT = NULL;
PFNGLXGETAGPOFFSETMESAPROC glad_glXGetAGPOffsetMESA = NULL;
PFNGLXGETCLIENTSTRINGPROC glad_glXGetClientString = NULL;
PFNGLXGETCONFIGPROC glad_glXGetConfig = NULL;
PFNGLXGETCONTEXTGPUIDAMDPROC glad_glXGetContextGPUIDAMD = NULL;
PFNGLXGETCONTEXTIDEXTPROC glad_glXGetContextIDEXT = NULL;
PFNGLXGETCURRENTASSOCIATEDCONTEXTAMDPROC glad_glXGetCurrentAssociatedContextAMD = NULL;
PFNGLXGETCURRENTCONTEXTPROC glad_glXGetCurrentContext = NULL;
PFNGLXGETCURRENTDISPLAYPROC glad_glXGetCurrentDisplay = NULL;
PFNGLXGETCURRENTDISPLAYEXTPROC glad_glXGetCurrentDisplayEXT = NULL;
PFNGLXGETCURRENTDRAWABLEPROC glad_glXGetCurrentDrawable = NULL;
PFNGLXGETCURRENTREADDRAWABLEPROC glad_glXGetCurrentReadDrawable = NULL;
PFNGLXGETCURRENTREADDRAWABLESGIPROC glad_glXGetCurrentReadDrawableSGI = NULL;
PFNGLXGETFBCONFIGATTRIBPROC glad_glXGetFBConfigAttrib = NULL;
PFNGLXGETFBCONFIGATTRIBSGIXPROC glad_glXGetFBConfigAttribSGIX = NULL;
PFNGLXGETFBCONFIGFROMVISUALSGIXPROC glad_glXGetFBConfigFromVisualSGIX = NULL;
PFNGLXGETFBCONFIGSPROC glad_glXGetFBConfigs = NULL;
PFNGLXGETGPUIDSAMDPROC glad_glXGetGPUIDsAMD = NULL;
PFNGLXGETGPUINFOAMDPROC glad_glXGetGPUInfoAMD = NULL;
PFNGLXGETMSCRATEOMLPROC glad_glXGetMscRateOML = NULL;
PFNGLXGETPROCADDRESSPROC glad_glXGetProcAddress = NULL;
PFNGLXGETPROCADDRESSARBPROC glad_glXGetProcAddressARB = NULL;
PFNGLXGETSELECTEDEVENTPROC glad_glXGetSelectedEvent = NULL;
PFNGLXGETSELECTEDEVENTSGIXPROC glad_glXGetSelectedEventSGIX = NULL;
PFNGLXGETSWAPINTERVALMESAPROC glad_glXGetSwapIntervalMESA = NULL;
PFNGLXGETSYNCVALUESOMLPROC glad_glXGetSyncValuesOML = NULL;
PFNGLXGETTRANSPARENTINDEXSUNPROC glad_glXGetTransparentIndexSUN = NULL;
PFNGLXGETVIDEODEVICENVPROC glad_glXGetVideoDeviceNV = NULL;
PFNGLXGETVIDEOINFONVPROC glad_glXGetVideoInfoNV = NULL;
PFNGLXGETVIDEOSYNCSGIPROC glad_glXGetVideoSyncSGI = NULL;
PFNGLXGETVISUALFROMFBCONFIGPROC glad_glXGetVisualFromFBConfig = NULL;
PFNGLXGETVISUALFROMFBCONFIGSGIXPROC glad_glXGetVisualFromFBConfigSGIX = NULL;
PFNGLXHYPERPIPEATTRIBSGIXPROC glad_glXHyperpipeAttribSGIX = NULL;
PFNGLXHYPERPIPECONFIGSGIXPROC glad_glXHyperpipeConfigSGIX = NULL;
PFNGLXIMPORTCONTEXTEXTPROC glad_glXImportContextEXT = NULL;
PFNGLXISDIRECTPROC glad_glXIsDirect = NULL;
PFNGLXJOINSWAPGROUPNVPROC glad_glXJoinSwapGroupNV = NULL;
PFNGLXJOINSWAPGROUPSGIXPROC glad_glXJoinSwapGroupSGIX = NULL;
PFNGLXLOCKVIDEOCAPTUREDEVICENVPROC glad_glXLockVideoCaptureDeviceNV = NULL;
PFNGLXMAKEASSOCIATEDCONTEXTCURRENTAMDPROC glad_glXMakeAssociatedContextCurrentAMD = NULL;
PFNGLXMAKECONTEXTCURRENTPROC glad_glXMakeContextCurrent = NULL;
PFNGLXMAKECURRENTPROC glad_glXMakeCurrent = NULL;
PFNGLXMAKECURRENTREADSGIPROC glad_glXMakeCurrentReadSGI = NULL;
PFNGLXNAMEDCOPYBUFFERSUBDATANVPROC glad_glXNamedCopyBufferSubDataNV = NULL;
PFNGLXQUERYCHANNELDELTASSGIXPROC glad_glXQueryChannelDeltasSGIX = NULL;
PFNGLXQUERYCHANNELRECTSGIXPROC glad_glXQueryChannelRectSGIX = NULL;
PFNGLXQUERYCONTEXTPROC glad_glXQueryContext = NULL;
PFNGLXQUERYCONTEXTINFOEXTPROC glad_glXQueryContextInfoEXT = NULL;
PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC glad_glXQueryCurrentRendererIntegerMESA = NULL;
PFNGLXQUERYCURRENTRENDERERSTRINGMESAPROC glad_glXQueryCurrentRendererStringMESA = NULL;
PFNGLXQUERYDRAWABLEPROC glad_glXQueryDrawable = NULL;
PFNGLXQUERYEXTENSIONPROC glad_glXQueryExtension = NULL;
PFNGLXQUERYEXTENSIONSSTRINGPROC glad_glXQueryExtensionsString = NULL;
PFNGLXQUERYFRAMECOUNTNVPROC glad_glXQueryFrameCountNV = NULL;
PFNGLXQUERYGLXPBUFFERSGIXPROC glad_glXQueryGLXPbufferSGIX = NULL;
PFNGLXQUERYHYPERPIPEATTRIBSGIXPROC glad_glXQueryHyperpipeAttribSGIX = NULL;
PFNGLXQUERYHYPERPIPEBESTATTRIBSGIXPROC glad_glXQueryHyperpipeBestAttribSGIX = NULL;
PFNGLXQUERYHYPERPIPECONFIGSGIXPROC glad_glXQueryHyperpipeConfigSGIX = NULL;
PFNGLXQUERYHYPERPIPENETWORKSGIXPROC glad_glXQueryHyperpipeNetworkSGIX = NULL;
PFNGLXQUERYMAXSWAPBARRIERSSGIXPROC glad_glXQueryMaxSwapBarriersSGIX = NULL;
PFNGLXQUERYMAXSWAPGROUPSNVPROC glad_glXQueryMaxSwapGroupsNV = NULL;
PFNGLXQUERYRENDERERINTEGERMESAPROC glad_glXQueryRendererIntegerMESA = NULL;
PFNGLXQUERYRENDERERSTRINGMESAPROC glad_glXQueryRendererStringMESA = NULL;
PFNGLXQUERYSERVERSTRINGPROC glad_glXQueryServerString = NULL;
PFNGLXQUERYSWAPGROUPNVPROC glad_glXQuerySwapGroupNV = NULL;
PFNGLXQUERYVERSIONPROC glad_glXQueryVersion = NULL;
PFNGLXQUERYVIDEOCAPTUREDEVICENVPROC glad_glXQueryVideoCaptureDeviceNV = NULL;
PFNGLXRELEASEBUFFERSMESAPROC glad_glXReleaseBuffersMESA = NULL;
PFNGLXRELEASETEXIMAGEEXTPROC glad_glXReleaseTexImageEXT = NULL;
PFNGLXRELEASEVIDEOCAPTUREDEVICENVPROC glad_glXReleaseVideoCaptureDeviceNV = NULL;
PFNGLXRELEASEVIDEODEVICENVPROC glad_glXReleaseVideoDeviceNV = NULL;
PFNGLXRELEASEVIDEOIMAGENVPROC glad_glXReleaseVideoImageNV = NULL;
PFNGLXRESETFRAMECOUNTNVPROC glad_glXResetFrameCountNV = NULL;
PFNGLXSELECTEVENTPROC glad_glXSelectEvent = NULL;
PFNGLXSELECTEVENTSGIXPROC glad_glXSelectEventSGIX = NULL;
PFNGLXSENDPBUFFERTOVIDEONVPROC glad_glXSendPbufferToVideoNV = NULL;
PFNGLXSET3DFXMODEMESAPROC glad_glXSet3DfxModeMESA = NULL;
PFNGLXSWAPBUFFERSPROC glad_glXSwapBuffers = NULL;
PFNGLXSWAPBUFFERSMSCOMLPROC glad_glXSwapBuffersMscOML = NULL;
PFNGLXSWAPINTERVALEXTPROC glad_glXSwapIntervalEXT = NULL;
PFNGLXSWAPINTERVALMESAPROC glad_glXSwapIntervalMESA = NULL;
PFNGLXSWAPINTERVALSGIPROC glad_glXSwapIntervalSGI = NULL;
PFNGLXUSEXFONTPROC glad_glXUseXFont = NULL;
PFNGLXWAITFORMSCOMLPROC glad_glXWaitForMscOML = NULL;
PFNGLXWAITFORSBCOMLPROC glad_glXWaitForSbcOML = NULL;
PFNGLXWAITGLPROC glad_glXWaitGL = NULL;
PFNGLXWAITVIDEOSYNCSGIPROC glad_glXWaitVideoSyncSGI = NULL;
PFNGLXWAITXPROC glad_glXWaitX = NULL;
static void glad_glx_load_GLX_VERSION_1_0( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_VERSION_1_0) return;
glad_glXChooseVisual = (PFNGLXCHOOSEVISUALPROC) load(userptr, "glXChooseVisual");
glad_glXCopyContext = (PFNGLXCOPYCONTEXTPROC) load(userptr, "glXCopyContext");
glad_glXCreateContext = (PFNGLXCREATECONTEXTPROC) load(userptr, "glXCreateContext");
glad_glXCreateGLXPixmap = (PFNGLXCREATEGLXPIXMAPPROC) load(userptr, "glXCreateGLXPixmap");
glad_glXDestroyContext = (PFNGLXDESTROYCONTEXTPROC) load(userptr, "glXDestroyContext");
glad_glXDestroyGLXPixmap = (PFNGLXDESTROYGLXPIXMAPPROC) load(userptr, "glXDestroyGLXPixmap");
glad_glXGetConfig = (PFNGLXGETCONFIGPROC) load(userptr, "glXGetConfig");
glad_glXGetCurrentContext = (PFNGLXGETCURRENTCONTEXTPROC) load(userptr, "glXGetCurrentContext");
glad_glXGetCurrentDrawable = (PFNGLXGETCURRENTDRAWABLEPROC) load(userptr, "glXGetCurrentDrawable");
glad_glXIsDirect = (PFNGLXISDIRECTPROC) load(userptr, "glXIsDirect");
glad_glXMakeCurrent = (PFNGLXMAKECURRENTPROC) load(userptr, "glXMakeCurrent");
glad_glXQueryExtension = (PFNGLXQUERYEXTENSIONPROC) load(userptr, "glXQueryExtension");
glad_glXQueryVersion = (PFNGLXQUERYVERSIONPROC) load(userptr, "glXQueryVersion");
glad_glXSwapBuffers = (PFNGLXSWAPBUFFERSPROC) load(userptr, "glXSwapBuffers");
glad_glXUseXFont = (PFNGLXUSEXFONTPROC) load(userptr, "glXUseXFont");
glad_glXWaitGL = (PFNGLXWAITGLPROC) load(userptr, "glXWaitGL");
glad_glXWaitX = (PFNGLXWAITXPROC) load(userptr, "glXWaitX");
}
static void glad_glx_load_GLX_VERSION_1_1( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_VERSION_1_1) return;
glad_glXGetClientString = (PFNGLXGETCLIENTSTRINGPROC) load(userptr, "glXGetClientString");
glad_glXQueryExtensionsString = (PFNGLXQUERYEXTENSIONSSTRINGPROC) load(userptr, "glXQueryExtensionsString");
glad_glXQueryServerString = (PFNGLXQUERYSERVERSTRINGPROC) load(userptr, "glXQueryServerString");
}
static void glad_glx_load_GLX_VERSION_1_2( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_VERSION_1_2) return;
glad_glXGetCurrentDisplay = (PFNGLXGETCURRENTDISPLAYPROC) load(userptr, "glXGetCurrentDisplay");
}
static void glad_glx_load_GLX_VERSION_1_3( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_VERSION_1_3) return;
glad_glXChooseFBConfig = (PFNGLXCHOOSEFBCONFIGPROC) load(userptr, "glXChooseFBConfig");
glad_glXCreateNewContext = (PFNGLXCREATENEWCONTEXTPROC) load(userptr, "glXCreateNewContext");
glad_glXCreatePbuffer = (PFNGLXCREATEPBUFFERPROC) load(userptr, "glXCreatePbuffer");
glad_glXCreatePixmap = (PFNGLXCREATEPIXMAPPROC) load(userptr, "glXCreatePixmap");
glad_glXCreateWindow = (PFNGLXCREATEWINDOWPROC) load(userptr, "glXCreateWindow");
glad_glXDestroyPbuffer = (PFNGLXDESTROYPBUFFERPROC) load(userptr, "glXDestroyPbuffer");
glad_glXDestroyPixmap = (PFNGLXDESTROYPIXMAPPROC) load(userptr, "glXDestroyPixmap");
glad_glXDestroyWindow = (PFNGLXDESTROYWINDOWPROC) load(userptr, "glXDestroyWindow");
glad_glXGetCurrentReadDrawable = (PFNGLXGETCURRENTREADDRAWABLEPROC) load(userptr, "glXGetCurrentReadDrawable");
glad_glXGetFBConfigAttrib = (PFNGLXGETFBCONFIGATTRIBPROC) load(userptr, "glXGetFBConfigAttrib");
glad_glXGetFBConfigs = (PFNGLXGETFBCONFIGSPROC) load(userptr, "glXGetFBConfigs");
glad_glXGetSelectedEvent = (PFNGLXGETSELECTEDEVENTPROC) load(userptr, "glXGetSelectedEvent");
glad_glXGetVisualFromFBConfig = (PFNGLXGETVISUALFROMFBCONFIGPROC) load(userptr, "glXGetVisualFromFBConfig");
glad_glXMakeContextCurrent = (PFNGLXMAKECONTEXTCURRENTPROC) load(userptr, "glXMakeContextCurrent");
glad_glXQueryContext = (PFNGLXQUERYCONTEXTPROC) load(userptr, "glXQueryContext");
glad_glXQueryDrawable = (PFNGLXQUERYDRAWABLEPROC) load(userptr, "glXQueryDrawable");
glad_glXSelectEvent = (PFNGLXSELECTEVENTPROC) load(userptr, "glXSelectEvent");
}
static void glad_glx_load_GLX_VERSION_1_4( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_VERSION_1_4) return;
glad_glXGetProcAddress = (PFNGLXGETPROCADDRESSPROC) load(userptr, "glXGetProcAddress");
}
static void glad_glx_load_GLX_AMD_gpu_association( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_AMD_gpu_association) return;
glad_glXBlitContextFramebufferAMD = (PFNGLXBLITCONTEXTFRAMEBUFFERAMDPROC) load(userptr, "glXBlitContextFramebufferAMD");
glad_glXCreateAssociatedContextAMD = (PFNGLXCREATEASSOCIATEDCONTEXTAMDPROC) load(userptr, "glXCreateAssociatedContextAMD");
glad_glXCreateAssociatedContextAttribsAMD = (PFNGLXCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC) load(userptr, "glXCreateAssociatedContextAttribsAMD");
glad_glXDeleteAssociatedContextAMD = (PFNGLXDELETEASSOCIATEDCONTEXTAMDPROC) load(userptr, "glXDeleteAssociatedContextAMD");
glad_glXGetContextGPUIDAMD = (PFNGLXGETCONTEXTGPUIDAMDPROC) load(userptr, "glXGetContextGPUIDAMD");
glad_glXGetCurrentAssociatedContextAMD = (PFNGLXGETCURRENTASSOCIATEDCONTEXTAMDPROC) load(userptr, "glXGetCurrentAssociatedContextAMD");
glad_glXGetGPUIDsAMD = (PFNGLXGETGPUIDSAMDPROC) load(userptr, "glXGetGPUIDsAMD");
glad_glXGetGPUInfoAMD = (PFNGLXGETGPUINFOAMDPROC) load(userptr, "glXGetGPUInfoAMD");
glad_glXMakeAssociatedContextCurrentAMD = (PFNGLXMAKEASSOCIATEDCONTEXTCURRENTAMDPROC) load(userptr, "glXMakeAssociatedContextCurrentAMD");
}
static void glad_glx_load_GLX_ARB_create_context( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_ARB_create_context) return;
glad_glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) load(userptr, "glXCreateContextAttribsARB");
}
static void glad_glx_load_GLX_ARB_get_proc_address( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_ARB_get_proc_address) return;
glad_glXGetProcAddressARB = (PFNGLXGETPROCADDRESSARBPROC) load(userptr, "glXGetProcAddressARB");
}
static void glad_glx_load_GLX_EXT_import_context( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_EXT_import_context) return;
glad_glXFreeContextEXT = (PFNGLXFREECONTEXTEXTPROC) load(userptr, "glXFreeContextEXT");
glad_glXGetContextIDEXT = (PFNGLXGETCONTEXTIDEXTPROC) load(userptr, "glXGetContextIDEXT");
glad_glXGetCurrentDisplayEXT = (PFNGLXGETCURRENTDISPLAYEXTPROC) load(userptr, "glXGetCurrentDisplayEXT");
glad_glXImportContextEXT = (PFNGLXIMPORTCONTEXTEXTPROC) load(userptr, "glXImportContextEXT");
glad_glXQueryContextInfoEXT = (PFNGLXQUERYCONTEXTINFOEXTPROC) load(userptr, "glXQueryContextInfoEXT");
}
static void glad_glx_load_GLX_EXT_swap_control( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_EXT_swap_control) return;
glad_glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) load(userptr, "glXSwapIntervalEXT");
}
static void glad_glx_load_GLX_EXT_texture_from_pixmap( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_EXT_texture_from_pixmap) return;
glad_glXBindTexImageEXT = (PFNGLXBINDTEXIMAGEEXTPROC) load(userptr, "glXBindTexImageEXT");
glad_glXReleaseTexImageEXT = (PFNGLXRELEASETEXIMAGEEXTPROC) load(userptr, "glXReleaseTexImageEXT");
}
static void glad_glx_load_GLX_MESA_agp_offset( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_MESA_agp_offset) return;
glad_glXGetAGPOffsetMESA = (PFNGLXGETAGPOFFSETMESAPROC) load(userptr, "glXGetAGPOffsetMESA");
}
static void glad_glx_load_GLX_MESA_copy_sub_buffer( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_MESA_copy_sub_buffer) return;
glad_glXCopySubBufferMESA = (PFNGLXCOPYSUBBUFFERMESAPROC) load(userptr, "glXCopySubBufferMESA");
}
static void glad_glx_load_GLX_MESA_pixmap_colormap( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_MESA_pixmap_colormap) return;
glad_glXCreateGLXPixmapMESA = (PFNGLXCREATEGLXPIXMAPMESAPROC) load(userptr, "glXCreateGLXPixmapMESA");
}
static void glad_glx_load_GLX_MESA_query_renderer( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_MESA_query_renderer) return;
glad_glXQueryCurrentRendererIntegerMESA = (PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC) load(userptr, "glXQueryCurrentRendererIntegerMESA");
glad_glXQueryCurrentRendererStringMESA = (PFNGLXQUERYCURRENTRENDERERSTRINGMESAPROC) load(userptr, "glXQueryCurrentRendererStringMESA");
glad_glXQueryRendererIntegerMESA = (PFNGLXQUERYRENDERERINTEGERMESAPROC) load(userptr, "glXQueryRendererIntegerMESA");
glad_glXQueryRendererStringMESA = (PFNGLXQUERYRENDERERSTRINGMESAPROC) load(userptr, "glXQueryRendererStringMESA");
}
static void glad_glx_load_GLX_MESA_release_buffers( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_MESA_release_buffers) return;
glad_glXReleaseBuffersMESA = (PFNGLXRELEASEBUFFERSMESAPROC) load(userptr, "glXReleaseBuffersMESA");
}
static void glad_glx_load_GLX_MESA_set_3dfx_mode( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_MESA_set_3dfx_mode) return;
glad_glXSet3DfxModeMESA = (PFNGLXSET3DFXMODEMESAPROC) load(userptr, "glXSet3DfxModeMESA");
}
static void glad_glx_load_GLX_MESA_swap_control( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_MESA_swap_control) return;
glad_glXGetSwapIntervalMESA = (PFNGLXGETSWAPINTERVALMESAPROC) load(userptr, "glXGetSwapIntervalMESA");
glad_glXSwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC) load(userptr, "glXSwapIntervalMESA");
}
static void glad_glx_load_GLX_NV_copy_buffer( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_NV_copy_buffer) return;
glad_glXCopyBufferSubDataNV = (PFNGLXCOPYBUFFERSUBDATANVPROC) load(userptr, "glXCopyBufferSubDataNV");
glad_glXNamedCopyBufferSubDataNV = (PFNGLXNAMEDCOPYBUFFERSUBDATANVPROC) load(userptr, "glXNamedCopyBufferSubDataNV");
}
static void glad_glx_load_GLX_NV_copy_image( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_NV_copy_image) return;
glad_glXCopyImageSubDataNV = (PFNGLXCOPYIMAGESUBDATANVPROC) load(userptr, "glXCopyImageSubDataNV");
}
static void glad_glx_load_GLX_NV_delay_before_swap( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_NV_delay_before_swap) return;
glad_glXDelayBeforeSwapNV = (PFNGLXDELAYBEFORESWAPNVPROC) load(userptr, "glXDelayBeforeSwapNV");
}
static void glad_glx_load_GLX_NV_present_video( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_NV_present_video) return;
glad_glXBindVideoDeviceNV = (PFNGLXBINDVIDEODEVICENVPROC) load(userptr, "glXBindVideoDeviceNV");
glad_glXEnumerateVideoDevicesNV = (PFNGLXENUMERATEVIDEODEVICESNVPROC) load(userptr, "glXEnumerateVideoDevicesNV");
}
static void glad_glx_load_GLX_NV_swap_group( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_NV_swap_group) return;
glad_glXBindSwapBarrierNV = (PFNGLXBINDSWAPBARRIERNVPROC) load(userptr, "glXBindSwapBarrierNV");
glad_glXJoinSwapGroupNV = (PFNGLXJOINSWAPGROUPNVPROC) load(userptr, "glXJoinSwapGroupNV");
glad_glXQueryFrameCountNV = (PFNGLXQUERYFRAMECOUNTNVPROC) load(userptr, "glXQueryFrameCountNV");
glad_glXQueryMaxSwapGroupsNV = (PFNGLXQUERYMAXSWAPGROUPSNVPROC) load(userptr, "glXQueryMaxSwapGroupsNV");
glad_glXQuerySwapGroupNV = (PFNGLXQUERYSWAPGROUPNVPROC) load(userptr, "glXQuerySwapGroupNV");
glad_glXResetFrameCountNV = (PFNGLXRESETFRAMECOUNTNVPROC) load(userptr, "glXResetFrameCountNV");
}
static void glad_glx_load_GLX_NV_video_capture( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_NV_video_capture) return;
glad_glXBindVideoCaptureDeviceNV = (PFNGLXBINDVIDEOCAPTUREDEVICENVPROC) load(userptr, "glXBindVideoCaptureDeviceNV");
glad_glXEnumerateVideoCaptureDevicesNV = (PFNGLXENUMERATEVIDEOCAPTUREDEVICESNVPROC) load(userptr, "glXEnumerateVideoCaptureDevicesNV");
glad_glXLockVideoCaptureDeviceNV = (PFNGLXLOCKVIDEOCAPTUREDEVICENVPROC) load(userptr, "glXLockVideoCaptureDeviceNV");
glad_glXQueryVideoCaptureDeviceNV = (PFNGLXQUERYVIDEOCAPTUREDEVICENVPROC) load(userptr, "glXQueryVideoCaptureDeviceNV");
glad_glXReleaseVideoCaptureDeviceNV = (PFNGLXRELEASEVIDEOCAPTUREDEVICENVPROC) load(userptr, "glXReleaseVideoCaptureDeviceNV");
}
static void glad_glx_load_GLX_NV_video_out( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_NV_video_out) return;
glad_glXBindVideoImageNV = (PFNGLXBINDVIDEOIMAGENVPROC) load(userptr, "glXBindVideoImageNV");
glad_glXGetVideoDeviceNV = (PFNGLXGETVIDEODEVICENVPROC) load(userptr, "glXGetVideoDeviceNV");
glad_glXGetVideoInfoNV = (PFNGLXGETVIDEOINFONVPROC) load(userptr, "glXGetVideoInfoNV");
glad_glXReleaseVideoDeviceNV = (PFNGLXRELEASEVIDEODEVICENVPROC) load(userptr, "glXReleaseVideoDeviceNV");
glad_glXReleaseVideoImageNV = (PFNGLXRELEASEVIDEOIMAGENVPROC) load(userptr, "glXReleaseVideoImageNV");
glad_glXSendPbufferToVideoNV = (PFNGLXSENDPBUFFERTOVIDEONVPROC) load(userptr, "glXSendPbufferToVideoNV");
}
static void glad_glx_load_GLX_OML_sync_control( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_OML_sync_control) return;
glad_glXGetMscRateOML = (PFNGLXGETMSCRATEOMLPROC) load(userptr, "glXGetMscRateOML");
glad_glXGetSyncValuesOML = (PFNGLXGETSYNCVALUESOMLPROC) load(userptr, "glXGetSyncValuesOML");
glad_glXSwapBuffersMscOML = (PFNGLXSWAPBUFFERSMSCOMLPROC) load(userptr, "glXSwapBuffersMscOML");
glad_glXWaitForMscOML = (PFNGLXWAITFORMSCOMLPROC) load(userptr, "glXWaitForMscOML");
glad_glXWaitForSbcOML = (PFNGLXWAITFORSBCOMLPROC) load(userptr, "glXWaitForSbcOML");
}
static void glad_glx_load_GLX_SGIX_fbconfig( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_SGIX_fbconfig) return;
glad_glXChooseFBConfigSGIX = (PFNGLXCHOOSEFBCONFIGSGIXPROC) load(userptr, "glXChooseFBConfigSGIX");
glad_glXCreateContextWithConfigSGIX = (PFNGLXCREATECONTEXTWITHCONFIGSGIXPROC) load(userptr, "glXCreateContextWithConfigSGIX");
glad_glXCreateGLXPixmapWithConfigSGIX = (PFNGLXCREATEGLXPIXMAPWITHCONFIGSGIXPROC) load(userptr, "glXCreateGLXPixmapWithConfigSGIX");
glad_glXGetFBConfigAttribSGIX = (PFNGLXGETFBCONFIGATTRIBSGIXPROC) load(userptr, "glXGetFBConfigAttribSGIX");
glad_glXGetFBConfigFromVisualSGIX = (PFNGLXGETFBCONFIGFROMVISUALSGIXPROC) load(userptr, "glXGetFBConfigFromVisualSGIX");
glad_glXGetVisualFromFBConfigSGIX = (PFNGLXGETVISUALFROMFBCONFIGSGIXPROC) load(userptr, "glXGetVisualFromFBConfigSGIX");
}
static void glad_glx_load_GLX_SGIX_hyperpipe( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_SGIX_hyperpipe) return;
glad_glXBindHyperpipeSGIX = (PFNGLXBINDHYPERPIPESGIXPROC) load(userptr, "glXBindHyperpipeSGIX");
glad_glXDestroyHyperpipeConfigSGIX = (PFNGLXDESTROYHYPERPIPECONFIGSGIXPROC) load(userptr, "glXDestroyHyperpipeConfigSGIX");
glad_glXHyperpipeAttribSGIX = (PFNGLXHYPERPIPEATTRIBSGIXPROC) load(userptr, "glXHyperpipeAttribSGIX");
glad_glXHyperpipeConfigSGIX = (PFNGLXHYPERPIPECONFIGSGIXPROC) load(userptr, "glXHyperpipeConfigSGIX");
glad_glXQueryHyperpipeAttribSGIX = (PFNGLXQUERYHYPERPIPEATTRIBSGIXPROC) load(userptr, "glXQueryHyperpipeAttribSGIX");
glad_glXQueryHyperpipeBestAttribSGIX = (PFNGLXQUERYHYPERPIPEBESTATTRIBSGIXPROC) load(userptr, "glXQueryHyperpipeBestAttribSGIX");
glad_glXQueryHyperpipeConfigSGIX = (PFNGLXQUERYHYPERPIPECONFIGSGIXPROC) load(userptr, "glXQueryHyperpipeConfigSGIX");
glad_glXQueryHyperpipeNetworkSGIX = (PFNGLXQUERYHYPERPIPENETWORKSGIXPROC) load(userptr, "glXQueryHyperpipeNetworkSGIX");
}
static void glad_glx_load_GLX_SGIX_pbuffer( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_SGIX_pbuffer) return;
glad_glXCreateGLXPbufferSGIX = (PFNGLXCREATEGLXPBUFFERSGIXPROC) load(userptr, "glXCreateGLXPbufferSGIX");
glad_glXDestroyGLXPbufferSGIX = (PFNGLXDESTROYGLXPBUFFERSGIXPROC) load(userptr, "glXDestroyGLXPbufferSGIX");
glad_glXGetSelectedEventSGIX = (PFNGLXGETSELECTEDEVENTSGIXPROC) load(userptr, "glXGetSelectedEventSGIX");
glad_glXQueryGLXPbufferSGIX = (PFNGLXQUERYGLXPBUFFERSGIXPROC) load(userptr, "glXQueryGLXPbufferSGIX");
glad_glXSelectEventSGIX = (PFNGLXSELECTEVENTSGIXPROC) load(userptr, "glXSelectEventSGIX");
}
static void glad_glx_load_GLX_SGIX_swap_barrier( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_SGIX_swap_barrier) return;
glad_glXBindSwapBarrierSGIX = (PFNGLXBINDSWAPBARRIERSGIXPROC) load(userptr, "glXBindSwapBarrierSGIX");
glad_glXQueryMaxSwapBarriersSGIX = (PFNGLXQUERYMAXSWAPBARRIERSSGIXPROC) load(userptr, "glXQueryMaxSwapBarriersSGIX");
}
static void glad_glx_load_GLX_SGIX_swap_group( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_SGIX_swap_group) return;
glad_glXJoinSwapGroupSGIX = (PFNGLXJOINSWAPGROUPSGIXPROC) load(userptr, "glXJoinSwapGroupSGIX");
}
static void glad_glx_load_GLX_SGIX_video_resize( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_SGIX_video_resize) return;
glad_glXBindChannelToWindowSGIX = (PFNGLXBINDCHANNELTOWINDOWSGIXPROC) load(userptr, "glXBindChannelToWindowSGIX");
glad_glXChannelRectSGIX = (PFNGLXCHANNELRECTSGIXPROC) load(userptr, "glXChannelRectSGIX");
glad_glXChannelRectSyncSGIX = (PFNGLXCHANNELRECTSYNCSGIXPROC) load(userptr, "glXChannelRectSyncSGIX");
glad_glXQueryChannelDeltasSGIX = (PFNGLXQUERYCHANNELDELTASSGIXPROC) load(userptr, "glXQueryChannelDeltasSGIX");
glad_glXQueryChannelRectSGIX = (PFNGLXQUERYCHANNELRECTSGIXPROC) load(userptr, "glXQueryChannelRectSGIX");
}
static void glad_glx_load_GLX_SGI_cushion( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_SGI_cushion) return;
glad_glXCushionSGI = (PFNGLXCUSHIONSGIPROC) load(userptr, "glXCushionSGI");
}
static void glad_glx_load_GLX_SGI_make_current_read( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_SGI_make_current_read) return;
glad_glXGetCurrentReadDrawableSGI = (PFNGLXGETCURRENTREADDRAWABLESGIPROC) load(userptr, "glXGetCurrentReadDrawableSGI");
glad_glXMakeCurrentReadSGI = (PFNGLXMAKECURRENTREADSGIPROC) load(userptr, "glXMakeCurrentReadSGI");
}
static void glad_glx_load_GLX_SGI_swap_control( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_SGI_swap_control) return;
glad_glXSwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC) load(userptr, "glXSwapIntervalSGI");
}
static void glad_glx_load_GLX_SGI_video_sync( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_SGI_video_sync) return;
glad_glXGetVideoSyncSGI = (PFNGLXGETVIDEOSYNCSGIPROC) load(userptr, "glXGetVideoSyncSGI");
glad_glXWaitVideoSyncSGI = (PFNGLXWAITVIDEOSYNCSGIPROC) load(userptr, "glXWaitVideoSyncSGI");
}
static void glad_glx_load_GLX_SUN_get_transparent_index( GLADuserptrloadfunc load, void* userptr) {
if(!GLAD_GLX_SUN_get_transparent_index) return;
glad_glXGetTransparentIndexSUN = (PFNGLXGETTRANSPARENTINDEXSUNPROC) load(userptr, "glXGetTransparentIndexSUN");
}
static void glad_glx_resolve_aliases(void) {
}
static int glad_glx_has_extension(Display *display, int screen, const char *ext) {
#ifndef GLX_VERSION_1_1
GLAD_UNUSED(display);
GLAD_UNUSED(screen);
GLAD_UNUSED(ext);
#else
const char *terminator;
const char *loc;
const char *extensions;
if (glXQueryExtensionsString == NULL) {
return 0;
}
extensions = glXQueryExtensionsString(display, screen);
if(extensions == NULL || ext == NULL) {
return 0;
}
while(1) {
loc = strstr(extensions, ext);
if(loc == NULL)
break;
terminator = loc + strlen(ext);
if((loc == extensions || *(loc - 1) == ' ') &&
(*terminator == ' ' || *terminator == '\0')) {
return 1;
}
extensions = terminator;
}
#endif
return 0;
}
static GLADapiproc glad_glx_get_proc_from_userptr(void *userptr, const char* name) {
return (GLAD_GNUC_EXTENSION (GLADapiproc (*)(const char *name)) userptr)(name);
}
static int glad_glx_find_extensions(Display *display, int screen) {
GLAD_GLX_3DFX_multisample = glad_glx_has_extension(display, screen, "GLX_3DFX_multisample");
GLAD_GLX_AMD_gpu_association = glad_glx_has_extension(display, screen, "GLX_AMD_gpu_association");
GLAD_GLX_ARB_context_flush_control = glad_glx_has_extension(display, screen, "GLX_ARB_context_flush_control");
GLAD_GLX_ARB_create_context = glad_glx_has_extension(display, screen, "GLX_ARB_create_context");
GLAD_GLX_ARB_create_context_no_error = glad_glx_has_extension(display, screen, "GLX_ARB_create_context_no_error");
GLAD_GLX_ARB_create_context_profile = glad_glx_has_extension(display, screen, "GLX_ARB_create_context_profile");
GLAD_GLX_ARB_create_context_robustness = glad_glx_has_extension(display, screen, "GLX_ARB_create_context_robustness");
GLAD_GLX_ARB_fbconfig_float = glad_glx_has_extension(display, screen, "GLX_ARB_fbconfig_float");
GLAD_GLX_ARB_framebuffer_sRGB = glad_glx_has_extension(display, screen, "GLX_ARB_framebuffer_sRGB");
GLAD_GLX_ARB_get_proc_address = glad_glx_has_extension(display, screen, "GLX_ARB_get_proc_address");
GLAD_GLX_ARB_multisample = glad_glx_has_extension(display, screen, "GLX_ARB_multisample");
GLAD_GLX_ARB_robustness_application_isolation = glad_glx_has_extension(display, screen, "GLX_ARB_robustness_application_isolation");
GLAD_GLX_ARB_robustness_share_group_isolation = glad_glx_has_extension(display, screen, "GLX_ARB_robustness_share_group_isolation");
GLAD_GLX_ARB_vertex_buffer_object = glad_glx_has_extension(display, screen, "GLX_ARB_vertex_buffer_object");
GLAD_GLX_EXT_buffer_age = glad_glx_has_extension(display, screen, "GLX_EXT_buffer_age");
GLAD_GLX_EXT_context_priority = glad_glx_has_extension(display, screen, "GLX_EXT_context_priority");
GLAD_GLX_EXT_create_context_es2_profile = glad_glx_has_extension(display, screen, "GLX_EXT_create_context_es2_profile");
GLAD_GLX_EXT_create_context_es_profile = glad_glx_has_extension(display, screen, "GLX_EXT_create_context_es_profile");
GLAD_GLX_EXT_fbconfig_packed_float = glad_glx_has_extension(display, screen, "GLX_EXT_fbconfig_packed_float");
GLAD_GLX_EXT_framebuffer_sRGB = glad_glx_has_extension(display, screen, "GLX_EXT_framebuffer_sRGB");
GLAD_GLX_EXT_get_drawable_type = glad_glx_has_extension(display, screen, "GLX_EXT_get_drawable_type");
GLAD_GLX_EXT_import_context = glad_glx_has_extension(display, screen, "GLX_EXT_import_context");
GLAD_GLX_EXT_libglvnd = glad_glx_has_extension(display, screen, "GLX_EXT_libglvnd");
GLAD_GLX_EXT_no_config_context = glad_glx_has_extension(display, screen, "GLX_EXT_no_config_context");
GLAD_GLX_EXT_stereo_tree = glad_glx_has_extension(display, screen, "GLX_EXT_stereo_tree");
GLAD_GLX_EXT_swap_control = glad_glx_has_extension(display, screen, "GLX_EXT_swap_control");
GLAD_GLX_EXT_swap_control_tear = glad_glx_has_extension(display, screen, "GLX_EXT_swap_control_tear");
GLAD_GLX_EXT_texture_from_pixmap = glad_glx_has_extension(display, screen, "GLX_EXT_texture_from_pixmap");
GLAD_GLX_EXT_visual_info = glad_glx_has_extension(display, screen, "GLX_EXT_visual_info");
GLAD_GLX_EXT_visual_rating = glad_glx_has_extension(display, screen, "GLX_EXT_visual_rating");
GLAD_GLX_INTEL_swap_event = glad_glx_has_extension(display, screen, "GLX_INTEL_swap_event");
GLAD_GLX_MESA_agp_offset = glad_glx_has_extension(display, screen, "GLX_MESA_agp_offset");
GLAD_GLX_MESA_copy_sub_buffer = glad_glx_has_extension(display, screen, "GLX_MESA_copy_sub_buffer");
GLAD_GLX_MESA_pixmap_colormap = glad_glx_has_extension(display, screen, "GLX_MESA_pixmap_colormap");
GLAD_GLX_MESA_query_renderer = glad_glx_has_extension(display, screen, "GLX_MESA_query_renderer");
GLAD_GLX_MESA_release_buffers = glad_glx_has_extension(display, screen, "GLX_MESA_release_buffers");
GLAD_GLX_MESA_set_3dfx_mode = glad_glx_has_extension(display, screen, "GLX_MESA_set_3dfx_mode");
GLAD_GLX_MESA_swap_control = glad_glx_has_extension(display, screen, "GLX_MESA_swap_control");
GLAD_GLX_NV_copy_buffer = glad_glx_has_extension(display, screen, "GLX_NV_copy_buffer");
GLAD_GLX_NV_copy_image = glad_glx_has_extension(display, screen, "GLX_NV_copy_image");
GLAD_GLX_NV_delay_before_swap = glad_glx_has_extension(display, screen, "GLX_NV_delay_before_swap");
GLAD_GLX_NV_float_buffer = glad_glx_has_extension(display, screen, "GLX_NV_float_buffer");
GLAD_GLX_NV_multigpu_context = glad_glx_has_extension(display, screen, "GLX_NV_multigpu_context");
GLAD_GLX_NV_multisample_coverage = glad_glx_has_extension(display, screen, "GLX_NV_multisample_coverage");
GLAD_GLX_NV_present_video = glad_glx_has_extension(display, screen, "GLX_NV_present_video");
GLAD_GLX_NV_robustness_video_memory_purge = glad_glx_has_extension(display, screen, "GLX_NV_robustness_video_memory_purge");
GLAD_GLX_NV_swap_group = glad_glx_has_extension(display, screen, "GLX_NV_swap_group");
GLAD_GLX_NV_video_capture = glad_glx_has_extension(display, screen, "GLX_NV_video_capture");
GLAD_GLX_NV_video_out = glad_glx_has_extension(display, screen, "GLX_NV_video_out");
GLAD_GLX_OML_swap_method = glad_glx_has_extension(display, screen, "GLX_OML_swap_method");
GLAD_GLX_OML_sync_control = glad_glx_has_extension(display, screen, "GLX_OML_sync_control");
GLAD_GLX_SGIS_blended_overlay = glad_glx_has_extension(display, screen, "GLX_SGIS_blended_overlay");
GLAD_GLX_SGIS_multisample = glad_glx_has_extension(display, screen, "GLX_SGIS_multisample");
GLAD_GLX_SGIS_shared_multisample = glad_glx_has_extension(display, screen, "GLX_SGIS_shared_multisample");
GLAD_GLX_SGIX_fbconfig = glad_glx_has_extension(display, screen, "GLX_SGIX_fbconfig");
GLAD_GLX_SGIX_hyperpipe = glad_glx_has_extension(display, screen, "GLX_SGIX_hyperpipe");
GLAD_GLX_SGIX_pbuffer = glad_glx_has_extension(display, screen, "GLX_SGIX_pbuffer");
GLAD_GLX_SGIX_swap_barrier = glad_glx_has_extension(display, screen, "GLX_SGIX_swap_barrier");
GLAD_GLX_SGIX_swap_group = glad_glx_has_extension(display, screen, "GLX_SGIX_swap_group");
GLAD_GLX_SGIX_video_resize = glad_glx_has_extension(display, screen, "GLX_SGIX_video_resize");
GLAD_GLX_SGIX_visual_select_group = glad_glx_has_extension(display, screen, "GLX_SGIX_visual_select_group");
GLAD_GLX_SGI_cushion = glad_glx_has_extension(display, screen, "GLX_SGI_cushion");
GLAD_GLX_SGI_make_current_read = glad_glx_has_extension(display, screen, "GLX_SGI_make_current_read");
GLAD_GLX_SGI_swap_control = glad_glx_has_extension(display, screen, "GLX_SGI_swap_control");
GLAD_GLX_SGI_video_sync = glad_glx_has_extension(display, screen, "GLX_SGI_video_sync");
GLAD_GLX_SUN_get_transparent_index = glad_glx_has_extension(display, screen, "GLX_SUN_get_transparent_index");
return 1;
}
static int glad_glx_find_core_glx(Display **display, int *screen) {
int major = 0, minor = 0;
if(*display == NULL) {
#ifdef GLAD_GLX_NO_X11
GLAD_UNUSED(screen);
return 0;
#else
*display = XOpenDisplay(0);
if (*display == NULL) {
return 0;
}
*screen = XScreenNumberOfScreen(XDefaultScreenOfDisplay(*display));
#endif
}
glXQueryVersion(*display, &major, &minor);
GLAD_GLX_VERSION_1_0 = (major == 1 && minor >= 0) || major > 1;
GLAD_GLX_VERSION_1_1 = (major == 1 && minor >= 1) || major > 1;
GLAD_GLX_VERSION_1_2 = (major == 1 && minor >= 2) || major > 1;
GLAD_GLX_VERSION_1_3 = (major == 1 && minor >= 3) || major > 1;
GLAD_GLX_VERSION_1_4 = (major == 1 && minor >= 4) || major > 1;
return GLAD_MAKE_VERSION(major, minor);
}
int gladLoadGLXUserPtr(Display *display, int screen, GLADuserptrloadfunc load, void *userptr) {
int version;
glXQueryVersion = (PFNGLXQUERYVERSIONPROC) load(userptr, "glXQueryVersion");
if(glXQueryVersion == NULL) return 0;
version = glad_glx_find_core_glx(&display, &screen);
glad_glx_load_GLX_VERSION_1_0(load, userptr);
glad_glx_load_GLX_VERSION_1_1(load, userptr);
glad_glx_load_GLX_VERSION_1_2(load, userptr);
glad_glx_load_GLX_VERSION_1_3(load, userptr);
glad_glx_load_GLX_VERSION_1_4(load, userptr);
if (!glad_glx_find_extensions(display, screen)) return 0;
glad_glx_load_GLX_AMD_gpu_association(load, userptr);
glad_glx_load_GLX_ARB_create_context(load, userptr);
glad_glx_load_GLX_ARB_get_proc_address(load, userptr);
glad_glx_load_GLX_EXT_import_context(load, userptr);
glad_glx_load_GLX_EXT_swap_control(load, userptr);
glad_glx_load_GLX_EXT_texture_from_pixmap(load, userptr);
glad_glx_load_GLX_MESA_agp_offset(load, userptr);
glad_glx_load_GLX_MESA_copy_sub_buffer(load, userptr);
glad_glx_load_GLX_MESA_pixmap_colormap(load, userptr);
glad_glx_load_GLX_MESA_query_renderer(load, userptr);
glad_glx_load_GLX_MESA_release_buffers(load, userptr);
glad_glx_load_GLX_MESA_set_3dfx_mode(load, userptr);
glad_glx_load_GLX_MESA_swap_control(load, userptr);
glad_glx_load_GLX_NV_copy_buffer(load, userptr);
glad_glx_load_GLX_NV_copy_image(load, userptr);
glad_glx_load_GLX_NV_delay_before_swap(load, userptr);
glad_glx_load_GLX_NV_present_video(load, userptr);
glad_glx_load_GLX_NV_swap_group(load, userptr);
glad_glx_load_GLX_NV_video_capture(load, userptr);
glad_glx_load_GLX_NV_video_out(load, userptr);
glad_glx_load_GLX_OML_sync_control(load, userptr);
glad_glx_load_GLX_SGIX_fbconfig(load, userptr);
glad_glx_load_GLX_SGIX_hyperpipe(load, userptr);
glad_glx_load_GLX_SGIX_pbuffer(load, userptr);
glad_glx_load_GLX_SGIX_swap_barrier(load, userptr);
glad_glx_load_GLX_SGIX_swap_group(load, userptr);
glad_glx_load_GLX_SGIX_video_resize(load, userptr);
glad_glx_load_GLX_SGI_cushion(load, userptr);
glad_glx_load_GLX_SGI_make_current_read(load, userptr);
glad_glx_load_GLX_SGI_swap_control(load, userptr);
glad_glx_load_GLX_SGI_video_sync(load, userptr);
glad_glx_load_GLX_SUN_get_transparent_index(load, userptr);
glad_glx_resolve_aliases();
return version;
}
int gladLoadGLX(Display *display, int screen, GLADloadfunc load) {
return gladLoadGLXUserPtr(display, screen, glad_glx_get_proc_from_userptr, GLAD_GNUC_EXTENSION (void*) load);
}
#ifdef GLAD_GLX
#ifndef GLAD_LOADER_LIBRARY_C_
#define GLAD_LOADER_LIBRARY_C_
#include <stddef.h>
#include <stdlib.h>
#if GLAD_PLATFORM_WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif
static void* glad_get_dlopen_handle(const char *lib_names[], int length) {
void *handle = NULL;
int i;
for (i = 0; i < length; ++i) {
#if GLAD_PLATFORM_WIN32
#if GLAD_PLATFORM_UWP
size_t buffer_size = (strlen(lib_names[i]) + 1) * sizeof(WCHAR);
LPWSTR buffer = (LPWSTR) malloc(buffer_size);
if (buffer != NULL) {
int ret = MultiByteToWideChar(CP_ACP, 0, lib_names[i], -1, buffer, buffer_size);
if (ret != 0) {
handle = (void*) LoadPackagedLibrary(buffer, 0);
}
free((void*) buffer);
}
#else
handle = (void*) LoadLibraryA(lib_names[i]);
#endif
#else
handle = dlopen(lib_names[i], RTLD_LAZY | RTLD_LOCAL);
#endif
if (handle != NULL) {
return handle;
}
}
return NULL;
}
static void glad_close_dlopen_handle(void* handle) {
if (handle != NULL) {
#if GLAD_PLATFORM_WIN32
FreeLibrary((HMODULE) handle);
#else
dlclose(handle);
#endif
}
}
static GLADapiproc glad_dlsym_handle(void* handle, const char *name) {
if (handle == NULL) {
return NULL;
}
#if GLAD_PLATFORM_WIN32
return (GLADapiproc) GetProcAddress((HMODULE) handle, name);
#else
return GLAD_GNUC_EXTENSION (GLADapiproc) dlsym(handle, name);
#endif
}
#endif /* GLAD_LOADER_LIBRARY_C_ */
typedef void* (GLAD_API_PTR *GLADglxprocaddrfunc)(const char*);
static GLADapiproc glad_glx_get_proc(void *userptr, const char *name) {
return GLAD_GNUC_EXTENSION ((GLADapiproc (*)(const char *name)) userptr)(name);
}
static void* _glx_handle;
static void* glad_glx_dlopen_handle(void) {
static const char *NAMES[] = {
#if defined __CYGWIN__
"libGL-1.so",
#endif
"libGL.so.1",
"libGL.so"
};
if (_glx_handle == NULL) {
_glx_handle = glad_get_dlopen_handle(NAMES, sizeof(NAMES) / sizeof(NAMES[0]));
}
return _glx_handle;
}
int gladLoaderLoadGLX(Display *display, int screen) {
int version = 0;
void *handle = NULL;
int did_load = 0;
GLADglxprocaddrfunc loader;
did_load = _glx_handle == NULL;
handle = glad_glx_dlopen_handle();
if (handle != NULL) {
loader = (GLADglxprocaddrfunc) glad_dlsym_handle(handle, "glXGetProcAddressARB");
if (loader != NULL) {
version = gladLoadGLXUserPtr(display, screen, glad_glx_get_proc, GLAD_GNUC_EXTENSION (void*) loader);
}
if (!version && did_load) {
gladLoaderUnloadGLX();
}
}
return version;
}
void gladLoaderUnloadGLX() {
if (_glx_handle != NULL) {
glad_close_dlopen_handle(_glx_handle);
_glx_handle = NULL;
}
}
#endif /* GLAD_GLX */
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,596 @@
/**
* SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <glad/wgl.h>
#ifndef GLAD_IMPL_UTIL_C_
#define GLAD_IMPL_UTIL_C_
#ifdef _MSC_VER
#define GLAD_IMPL_UTIL_SSCANF sscanf_s
#else
#define GLAD_IMPL_UTIL_SSCANF sscanf
#endif
#endif /* GLAD_IMPL_UTIL_C_ */
#ifdef __cplusplus
extern "C" {
#endif
int GLAD_WGL_VERSION_1_0 = 0;
int GLAD_WGL_3DFX_multisample = 0;
int GLAD_WGL_3DL_stereo_control = 0;
int GLAD_WGL_AMD_gpu_association = 0;
int GLAD_WGL_ARB_buffer_region = 0;
int GLAD_WGL_ARB_context_flush_control = 0;
int GLAD_WGL_ARB_create_context = 0;
int GLAD_WGL_ARB_create_context_no_error = 0;
int GLAD_WGL_ARB_create_context_profile = 0;
int GLAD_WGL_ARB_create_context_robustness = 0;
int GLAD_WGL_ARB_extensions_string = 0;
int GLAD_WGL_ARB_framebuffer_sRGB = 0;
int GLAD_WGL_ARB_make_current_read = 0;
int GLAD_WGL_ARB_multisample = 0;
int GLAD_WGL_ARB_pbuffer = 0;
int GLAD_WGL_ARB_pixel_format = 0;
int GLAD_WGL_ARB_pixel_format_float = 0;
int GLAD_WGL_ARB_render_texture = 0;
int GLAD_WGL_ARB_robustness_application_isolation = 0;
int GLAD_WGL_ARB_robustness_share_group_isolation = 0;
int GLAD_WGL_ATI_pixel_format_float = 0;
int GLAD_WGL_ATI_render_texture_rectangle = 0;
int GLAD_WGL_EXT_colorspace = 0;
int GLAD_WGL_EXT_create_context_es2_profile = 0;
int GLAD_WGL_EXT_create_context_es_profile = 0;
int GLAD_WGL_EXT_depth_float = 0;
int GLAD_WGL_EXT_display_color_table = 0;
int GLAD_WGL_EXT_extensions_string = 0;
int GLAD_WGL_EXT_framebuffer_sRGB = 0;
int GLAD_WGL_EXT_make_current_read = 0;
int GLAD_WGL_EXT_multisample = 0;
int GLAD_WGL_EXT_pbuffer = 0;
int GLAD_WGL_EXT_pixel_format = 0;
int GLAD_WGL_EXT_pixel_format_packed_float = 0;
int GLAD_WGL_EXT_swap_control = 0;
int GLAD_WGL_EXT_swap_control_tear = 0;
int GLAD_WGL_I3D_digital_video_control = 0;
int GLAD_WGL_I3D_gamma = 0;
int GLAD_WGL_I3D_genlock = 0;
int GLAD_WGL_I3D_image_buffer = 0;
int GLAD_WGL_I3D_swap_frame_lock = 0;
int GLAD_WGL_I3D_swap_frame_usage = 0;
int GLAD_WGL_NV_DX_interop = 0;
int GLAD_WGL_NV_DX_interop2 = 0;
int GLAD_WGL_NV_copy_image = 0;
int GLAD_WGL_NV_delay_before_swap = 0;
int GLAD_WGL_NV_float_buffer = 0;
int GLAD_WGL_NV_gpu_affinity = 0;
int GLAD_WGL_NV_multigpu_context = 0;
int GLAD_WGL_NV_multisample_coverage = 0;
int GLAD_WGL_NV_present_video = 0;
int GLAD_WGL_NV_render_depth_texture = 0;
int GLAD_WGL_NV_render_texture_rectangle = 0;
int GLAD_WGL_NV_swap_group = 0;
int GLAD_WGL_NV_vertex_array_range = 0;
int GLAD_WGL_NV_video_capture = 0;
int GLAD_WGL_NV_video_output = 0;
int GLAD_WGL_OML_sync_control = 0;
PFNWGLALLOCATEMEMORYNVPROC glad_wglAllocateMemoryNV = NULL;
PFNWGLASSOCIATEIMAGEBUFFEREVENTSI3DPROC glad_wglAssociateImageBufferEventsI3D = NULL;
PFNWGLBEGINFRAMETRACKINGI3DPROC glad_wglBeginFrameTrackingI3D = NULL;
PFNWGLBINDDISPLAYCOLORTABLEEXTPROC glad_wglBindDisplayColorTableEXT = NULL;
PFNWGLBINDSWAPBARRIERNVPROC glad_wglBindSwapBarrierNV = NULL;
PFNWGLBINDTEXIMAGEARBPROC glad_wglBindTexImageARB = NULL;
PFNWGLBINDVIDEOCAPTUREDEVICENVPROC glad_wglBindVideoCaptureDeviceNV = NULL;
PFNWGLBINDVIDEODEVICENVPROC glad_wglBindVideoDeviceNV = NULL;
PFNWGLBINDVIDEOIMAGENVPROC glad_wglBindVideoImageNV = NULL;
PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC glad_wglBlitContextFramebufferAMD = NULL;
PFNWGLCHOOSEPIXELFORMATARBPROC glad_wglChoosePixelFormatARB = NULL;
PFNWGLCHOOSEPIXELFORMATEXTPROC glad_wglChoosePixelFormatEXT = NULL;
PFNWGLCOPYIMAGESUBDATANVPROC glad_wglCopyImageSubDataNV = NULL;
PFNWGLCREATEAFFINITYDCNVPROC glad_wglCreateAffinityDCNV = NULL;
PFNWGLCREATEASSOCIATEDCONTEXTAMDPROC glad_wglCreateAssociatedContextAMD = NULL;
PFNWGLCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC glad_wglCreateAssociatedContextAttribsAMD = NULL;
PFNWGLCREATEBUFFERREGIONARBPROC glad_wglCreateBufferRegionARB = NULL;
PFNWGLCREATECONTEXTATTRIBSARBPROC glad_wglCreateContextAttribsARB = NULL;
PFNWGLCREATEDISPLAYCOLORTABLEEXTPROC glad_wglCreateDisplayColorTableEXT = NULL;
PFNWGLCREATEIMAGEBUFFERI3DPROC glad_wglCreateImageBufferI3D = NULL;
PFNWGLCREATEPBUFFERARBPROC glad_wglCreatePbufferARB = NULL;
PFNWGLCREATEPBUFFEREXTPROC glad_wglCreatePbufferEXT = NULL;
PFNWGLDXCLOSEDEVICENVPROC glad_wglDXCloseDeviceNV = NULL;
PFNWGLDXLOCKOBJECTSNVPROC glad_wglDXLockObjectsNV = NULL;
PFNWGLDXOBJECTACCESSNVPROC glad_wglDXObjectAccessNV = NULL;
PFNWGLDXOPENDEVICENVPROC glad_wglDXOpenDeviceNV = NULL;
PFNWGLDXREGISTEROBJECTNVPROC glad_wglDXRegisterObjectNV = NULL;
PFNWGLDXSETRESOURCESHAREHANDLENVPROC glad_wglDXSetResourceShareHandleNV = NULL;
PFNWGLDXUNLOCKOBJECTSNVPROC glad_wglDXUnlockObjectsNV = NULL;
PFNWGLDXUNREGISTEROBJECTNVPROC glad_wglDXUnregisterObjectNV = NULL;
PFNWGLDELAYBEFORESWAPNVPROC glad_wglDelayBeforeSwapNV = NULL;
PFNWGLDELETEASSOCIATEDCONTEXTAMDPROC glad_wglDeleteAssociatedContextAMD = NULL;
PFNWGLDELETEBUFFERREGIONARBPROC glad_wglDeleteBufferRegionARB = NULL;
PFNWGLDELETEDCNVPROC glad_wglDeleteDCNV = NULL;
PFNWGLDESTROYDISPLAYCOLORTABLEEXTPROC glad_wglDestroyDisplayColorTableEXT = NULL;
PFNWGLDESTROYIMAGEBUFFERI3DPROC glad_wglDestroyImageBufferI3D = NULL;
PFNWGLDESTROYPBUFFERARBPROC glad_wglDestroyPbufferARB = NULL;
PFNWGLDESTROYPBUFFEREXTPROC glad_wglDestroyPbufferEXT = NULL;
PFNWGLDISABLEFRAMELOCKI3DPROC glad_wglDisableFrameLockI3D = NULL;
PFNWGLDISABLEGENLOCKI3DPROC glad_wglDisableGenlockI3D = NULL;
PFNWGLENABLEFRAMELOCKI3DPROC glad_wglEnableFrameLockI3D = NULL;
PFNWGLENABLEGENLOCKI3DPROC glad_wglEnableGenlockI3D = NULL;
PFNWGLENDFRAMETRACKINGI3DPROC glad_wglEndFrameTrackingI3D = NULL;
PFNWGLENUMGPUDEVICESNVPROC glad_wglEnumGpuDevicesNV = NULL;
PFNWGLENUMGPUSFROMAFFINITYDCNVPROC glad_wglEnumGpusFromAffinityDCNV = NULL;
PFNWGLENUMGPUSNVPROC glad_wglEnumGpusNV = NULL;
PFNWGLENUMERATEVIDEOCAPTUREDEVICESNVPROC glad_wglEnumerateVideoCaptureDevicesNV = NULL;
PFNWGLENUMERATEVIDEODEVICESNVPROC glad_wglEnumerateVideoDevicesNV = NULL;
PFNWGLFREEMEMORYNVPROC glad_wglFreeMemoryNV = NULL;
PFNWGLGENLOCKSAMPLERATEI3DPROC glad_wglGenlockSampleRateI3D = NULL;
PFNWGLGENLOCKSOURCEDELAYI3DPROC glad_wglGenlockSourceDelayI3D = NULL;
PFNWGLGENLOCKSOURCEEDGEI3DPROC glad_wglGenlockSourceEdgeI3D = NULL;
PFNWGLGENLOCKSOURCEI3DPROC glad_wglGenlockSourceI3D = NULL;
PFNWGLGETCONTEXTGPUIDAMDPROC glad_wglGetContextGPUIDAMD = NULL;
PFNWGLGETCURRENTASSOCIATEDCONTEXTAMDPROC glad_wglGetCurrentAssociatedContextAMD = NULL;
PFNWGLGETCURRENTREADDCARBPROC glad_wglGetCurrentReadDCARB = NULL;
PFNWGLGETCURRENTREADDCEXTPROC glad_wglGetCurrentReadDCEXT = NULL;
PFNWGLGETDIGITALVIDEOPARAMETERSI3DPROC glad_wglGetDigitalVideoParametersI3D = NULL;
PFNWGLGETEXTENSIONSSTRINGARBPROC glad_wglGetExtensionsStringARB = NULL;
PFNWGLGETEXTENSIONSSTRINGEXTPROC glad_wglGetExtensionsStringEXT = NULL;
PFNWGLGETFRAMEUSAGEI3DPROC glad_wglGetFrameUsageI3D = NULL;
PFNWGLGETGPUIDSAMDPROC glad_wglGetGPUIDsAMD = NULL;
PFNWGLGETGPUINFOAMDPROC glad_wglGetGPUInfoAMD = NULL;
PFNWGLGETGAMMATABLEI3DPROC glad_wglGetGammaTableI3D = NULL;
PFNWGLGETGAMMATABLEPARAMETERSI3DPROC glad_wglGetGammaTableParametersI3D = NULL;
PFNWGLGETGENLOCKSAMPLERATEI3DPROC glad_wglGetGenlockSampleRateI3D = NULL;
PFNWGLGETGENLOCKSOURCEDELAYI3DPROC glad_wglGetGenlockSourceDelayI3D = NULL;
PFNWGLGETGENLOCKSOURCEEDGEI3DPROC glad_wglGetGenlockSourceEdgeI3D = NULL;
PFNWGLGETGENLOCKSOURCEI3DPROC glad_wglGetGenlockSourceI3D = NULL;
PFNWGLGETMSCRATEOMLPROC glad_wglGetMscRateOML = NULL;
PFNWGLGETPBUFFERDCARBPROC glad_wglGetPbufferDCARB = NULL;
PFNWGLGETPBUFFERDCEXTPROC glad_wglGetPbufferDCEXT = NULL;
PFNWGLGETPIXELFORMATATTRIBFVARBPROC glad_wglGetPixelFormatAttribfvARB = NULL;
PFNWGLGETPIXELFORMATATTRIBFVEXTPROC glad_wglGetPixelFormatAttribfvEXT = NULL;
PFNWGLGETPIXELFORMATATTRIBIVARBPROC glad_wglGetPixelFormatAttribivARB = NULL;
PFNWGLGETPIXELFORMATATTRIBIVEXTPROC glad_wglGetPixelFormatAttribivEXT = NULL;
PFNWGLGETSWAPINTERVALEXTPROC glad_wglGetSwapIntervalEXT = NULL;
PFNWGLGETSYNCVALUESOMLPROC glad_wglGetSyncValuesOML = NULL;
PFNWGLGETVIDEODEVICENVPROC glad_wglGetVideoDeviceNV = NULL;
PFNWGLGETVIDEOINFONVPROC glad_wglGetVideoInfoNV = NULL;
PFNWGLISENABLEDFRAMELOCKI3DPROC glad_wglIsEnabledFrameLockI3D = NULL;
PFNWGLISENABLEDGENLOCKI3DPROC glad_wglIsEnabledGenlockI3D = NULL;
PFNWGLJOINSWAPGROUPNVPROC glad_wglJoinSwapGroupNV = NULL;
PFNWGLLOADDISPLAYCOLORTABLEEXTPROC glad_wglLoadDisplayColorTableEXT = NULL;
PFNWGLLOCKVIDEOCAPTUREDEVICENVPROC glad_wglLockVideoCaptureDeviceNV = NULL;
PFNWGLMAKEASSOCIATEDCONTEXTCURRENTAMDPROC glad_wglMakeAssociatedContextCurrentAMD = NULL;
PFNWGLMAKECONTEXTCURRENTARBPROC glad_wglMakeContextCurrentARB = NULL;
PFNWGLMAKECONTEXTCURRENTEXTPROC glad_wglMakeContextCurrentEXT = NULL;
PFNWGLQUERYCURRENTCONTEXTNVPROC glad_wglQueryCurrentContextNV = NULL;
PFNWGLQUERYFRAMECOUNTNVPROC glad_wglQueryFrameCountNV = NULL;
PFNWGLQUERYFRAMELOCKMASTERI3DPROC glad_wglQueryFrameLockMasterI3D = NULL;
PFNWGLQUERYFRAMETRACKINGI3DPROC glad_wglQueryFrameTrackingI3D = NULL;
PFNWGLQUERYGENLOCKMAXSOURCEDELAYI3DPROC glad_wglQueryGenlockMaxSourceDelayI3D = NULL;
PFNWGLQUERYMAXSWAPGROUPSNVPROC glad_wglQueryMaxSwapGroupsNV = NULL;
PFNWGLQUERYPBUFFERARBPROC glad_wglQueryPbufferARB = NULL;
PFNWGLQUERYPBUFFEREXTPROC glad_wglQueryPbufferEXT = NULL;
PFNWGLQUERYSWAPGROUPNVPROC glad_wglQuerySwapGroupNV = NULL;
PFNWGLQUERYVIDEOCAPTUREDEVICENVPROC glad_wglQueryVideoCaptureDeviceNV = NULL;
PFNWGLRELEASEIMAGEBUFFEREVENTSI3DPROC glad_wglReleaseImageBufferEventsI3D = NULL;
PFNWGLRELEASEPBUFFERDCARBPROC glad_wglReleasePbufferDCARB = NULL;
PFNWGLRELEASEPBUFFERDCEXTPROC glad_wglReleasePbufferDCEXT = NULL;
PFNWGLRELEASETEXIMAGEARBPROC glad_wglReleaseTexImageARB = NULL;
PFNWGLRELEASEVIDEOCAPTUREDEVICENVPROC glad_wglReleaseVideoCaptureDeviceNV = NULL;
PFNWGLRELEASEVIDEODEVICENVPROC glad_wglReleaseVideoDeviceNV = NULL;
PFNWGLRELEASEVIDEOIMAGENVPROC glad_wglReleaseVideoImageNV = NULL;
PFNWGLRESETFRAMECOUNTNVPROC glad_wglResetFrameCountNV = NULL;
PFNWGLRESTOREBUFFERREGIONARBPROC glad_wglRestoreBufferRegionARB = NULL;
PFNWGLSAVEBUFFERREGIONARBPROC glad_wglSaveBufferRegionARB = NULL;
PFNWGLSENDPBUFFERTOVIDEONVPROC glad_wglSendPbufferToVideoNV = NULL;
PFNWGLSETDIGITALVIDEOPARAMETERSI3DPROC glad_wglSetDigitalVideoParametersI3D = NULL;
PFNWGLSETGAMMATABLEI3DPROC glad_wglSetGammaTableI3D = NULL;
PFNWGLSETGAMMATABLEPARAMETERSI3DPROC glad_wglSetGammaTableParametersI3D = NULL;
PFNWGLSETPBUFFERATTRIBARBPROC glad_wglSetPbufferAttribARB = NULL;
PFNWGLSETSTEREOEMITTERSTATE3DLPROC glad_wglSetStereoEmitterState3DL = NULL;
PFNWGLSWAPBUFFERSMSCOMLPROC glad_wglSwapBuffersMscOML = NULL;
PFNWGLSWAPINTERVALEXTPROC glad_wglSwapIntervalEXT = NULL;
PFNWGLSWAPLAYERBUFFERSMSCOMLPROC glad_wglSwapLayerBuffersMscOML = NULL;
PFNWGLWAITFORMSCOMLPROC glad_wglWaitForMscOML = NULL;
PFNWGLWAITFORSBCOMLPROC glad_wglWaitForSbcOML = NULL;
static void glad_wgl_load_WGL_3DL_stereo_control(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_3DL_stereo_control) return;
glad_wglSetStereoEmitterState3DL = (PFNWGLSETSTEREOEMITTERSTATE3DLPROC) load(userptr, "wglSetStereoEmitterState3DL");
}
static void glad_wgl_load_WGL_AMD_gpu_association(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_AMD_gpu_association) return;
glad_wglBlitContextFramebufferAMD = (PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC) load(userptr, "wglBlitContextFramebufferAMD");
glad_wglCreateAssociatedContextAMD = (PFNWGLCREATEASSOCIATEDCONTEXTAMDPROC) load(userptr, "wglCreateAssociatedContextAMD");
glad_wglCreateAssociatedContextAttribsAMD = (PFNWGLCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC) load(userptr, "wglCreateAssociatedContextAttribsAMD");
glad_wglDeleteAssociatedContextAMD = (PFNWGLDELETEASSOCIATEDCONTEXTAMDPROC) load(userptr, "wglDeleteAssociatedContextAMD");
glad_wglGetContextGPUIDAMD = (PFNWGLGETCONTEXTGPUIDAMDPROC) load(userptr, "wglGetContextGPUIDAMD");
glad_wglGetCurrentAssociatedContextAMD = (PFNWGLGETCURRENTASSOCIATEDCONTEXTAMDPROC) load(userptr, "wglGetCurrentAssociatedContextAMD");
glad_wglGetGPUIDsAMD = (PFNWGLGETGPUIDSAMDPROC) load(userptr, "wglGetGPUIDsAMD");
glad_wglGetGPUInfoAMD = (PFNWGLGETGPUINFOAMDPROC) load(userptr, "wglGetGPUInfoAMD");
glad_wglMakeAssociatedContextCurrentAMD = (PFNWGLMAKEASSOCIATEDCONTEXTCURRENTAMDPROC) load(userptr, "wglMakeAssociatedContextCurrentAMD");
}
static void glad_wgl_load_WGL_ARB_buffer_region(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_ARB_buffer_region) return;
glad_wglCreateBufferRegionARB = (PFNWGLCREATEBUFFERREGIONARBPROC) load(userptr, "wglCreateBufferRegionARB");
glad_wglDeleteBufferRegionARB = (PFNWGLDELETEBUFFERREGIONARBPROC) load(userptr, "wglDeleteBufferRegionARB");
glad_wglRestoreBufferRegionARB = (PFNWGLRESTOREBUFFERREGIONARBPROC) load(userptr, "wglRestoreBufferRegionARB");
glad_wglSaveBufferRegionARB = (PFNWGLSAVEBUFFERREGIONARBPROC) load(userptr, "wglSaveBufferRegionARB");
}
static void glad_wgl_load_WGL_ARB_create_context(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_ARB_create_context) return;
glad_wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) load(userptr, "wglCreateContextAttribsARB");
}
static void glad_wgl_load_WGL_ARB_extensions_string(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_ARB_extensions_string) return;
glad_wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC) load(userptr, "wglGetExtensionsStringARB");
}
static void glad_wgl_load_WGL_ARB_make_current_read(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_ARB_make_current_read) return;
glad_wglGetCurrentReadDCARB = (PFNWGLGETCURRENTREADDCARBPROC) load(userptr, "wglGetCurrentReadDCARB");
glad_wglMakeContextCurrentARB = (PFNWGLMAKECONTEXTCURRENTARBPROC) load(userptr, "wglMakeContextCurrentARB");
}
static void glad_wgl_load_WGL_ARB_pbuffer(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_ARB_pbuffer) return;
glad_wglCreatePbufferARB = (PFNWGLCREATEPBUFFERARBPROC) load(userptr, "wglCreatePbufferARB");
glad_wglDestroyPbufferARB = (PFNWGLDESTROYPBUFFERARBPROC) load(userptr, "wglDestroyPbufferARB");
glad_wglGetPbufferDCARB = (PFNWGLGETPBUFFERDCARBPROC) load(userptr, "wglGetPbufferDCARB");
glad_wglQueryPbufferARB = (PFNWGLQUERYPBUFFERARBPROC) load(userptr, "wglQueryPbufferARB");
glad_wglReleasePbufferDCARB = (PFNWGLRELEASEPBUFFERDCARBPROC) load(userptr, "wglReleasePbufferDCARB");
}
static void glad_wgl_load_WGL_ARB_pixel_format(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_ARB_pixel_format) return;
glad_wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC) load(userptr, "wglChoosePixelFormatARB");
glad_wglGetPixelFormatAttribfvARB = (PFNWGLGETPIXELFORMATATTRIBFVARBPROC) load(userptr, "wglGetPixelFormatAttribfvARB");
glad_wglGetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC) load(userptr, "wglGetPixelFormatAttribivARB");
}
static void glad_wgl_load_WGL_ARB_render_texture(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_ARB_render_texture) return;
glad_wglBindTexImageARB = (PFNWGLBINDTEXIMAGEARBPROC) load(userptr, "wglBindTexImageARB");
glad_wglReleaseTexImageARB = (PFNWGLRELEASETEXIMAGEARBPROC) load(userptr, "wglReleaseTexImageARB");
glad_wglSetPbufferAttribARB = (PFNWGLSETPBUFFERATTRIBARBPROC) load(userptr, "wglSetPbufferAttribARB");
}
static void glad_wgl_load_WGL_EXT_display_color_table(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_EXT_display_color_table) return;
glad_wglBindDisplayColorTableEXT = (PFNWGLBINDDISPLAYCOLORTABLEEXTPROC) load(userptr, "wglBindDisplayColorTableEXT");
glad_wglCreateDisplayColorTableEXT = (PFNWGLCREATEDISPLAYCOLORTABLEEXTPROC) load(userptr, "wglCreateDisplayColorTableEXT");
glad_wglDestroyDisplayColorTableEXT = (PFNWGLDESTROYDISPLAYCOLORTABLEEXTPROC) load(userptr, "wglDestroyDisplayColorTableEXT");
glad_wglLoadDisplayColorTableEXT = (PFNWGLLOADDISPLAYCOLORTABLEEXTPROC) load(userptr, "wglLoadDisplayColorTableEXT");
}
static void glad_wgl_load_WGL_EXT_extensions_string(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_EXT_extensions_string) return;
glad_wglGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC) load(userptr, "wglGetExtensionsStringEXT");
}
static void glad_wgl_load_WGL_EXT_make_current_read(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_EXT_make_current_read) return;
glad_wglGetCurrentReadDCEXT = (PFNWGLGETCURRENTREADDCEXTPROC) load(userptr, "wglGetCurrentReadDCEXT");
glad_wglMakeContextCurrentEXT = (PFNWGLMAKECONTEXTCURRENTEXTPROC) load(userptr, "wglMakeContextCurrentEXT");
}
static void glad_wgl_load_WGL_EXT_pbuffer(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_EXT_pbuffer) return;
glad_wglCreatePbufferEXT = (PFNWGLCREATEPBUFFEREXTPROC) load(userptr, "wglCreatePbufferEXT");
glad_wglDestroyPbufferEXT = (PFNWGLDESTROYPBUFFEREXTPROC) load(userptr, "wglDestroyPbufferEXT");
glad_wglGetPbufferDCEXT = (PFNWGLGETPBUFFERDCEXTPROC) load(userptr, "wglGetPbufferDCEXT");
glad_wglQueryPbufferEXT = (PFNWGLQUERYPBUFFEREXTPROC) load(userptr, "wglQueryPbufferEXT");
glad_wglReleasePbufferDCEXT = (PFNWGLRELEASEPBUFFERDCEXTPROC) load(userptr, "wglReleasePbufferDCEXT");
}
static void glad_wgl_load_WGL_EXT_pixel_format(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_EXT_pixel_format) return;
glad_wglChoosePixelFormatEXT = (PFNWGLCHOOSEPIXELFORMATEXTPROC) load(userptr, "wglChoosePixelFormatEXT");
glad_wglGetPixelFormatAttribfvEXT = (PFNWGLGETPIXELFORMATATTRIBFVEXTPROC) load(userptr, "wglGetPixelFormatAttribfvEXT");
glad_wglGetPixelFormatAttribivEXT = (PFNWGLGETPIXELFORMATATTRIBIVEXTPROC) load(userptr, "wglGetPixelFormatAttribivEXT");
}
static void glad_wgl_load_WGL_EXT_swap_control(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_EXT_swap_control) return;
glad_wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC) load(userptr, "wglGetSwapIntervalEXT");
glad_wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) load(userptr, "wglSwapIntervalEXT");
}
static void glad_wgl_load_WGL_I3D_digital_video_control(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_I3D_digital_video_control) return;
glad_wglGetDigitalVideoParametersI3D = (PFNWGLGETDIGITALVIDEOPARAMETERSI3DPROC) load(userptr, "wglGetDigitalVideoParametersI3D");
glad_wglSetDigitalVideoParametersI3D = (PFNWGLSETDIGITALVIDEOPARAMETERSI3DPROC) load(userptr, "wglSetDigitalVideoParametersI3D");
}
static void glad_wgl_load_WGL_I3D_gamma(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_I3D_gamma) return;
glad_wglGetGammaTableI3D = (PFNWGLGETGAMMATABLEI3DPROC) load(userptr, "wglGetGammaTableI3D");
glad_wglGetGammaTableParametersI3D = (PFNWGLGETGAMMATABLEPARAMETERSI3DPROC) load(userptr, "wglGetGammaTableParametersI3D");
glad_wglSetGammaTableI3D = (PFNWGLSETGAMMATABLEI3DPROC) load(userptr, "wglSetGammaTableI3D");
glad_wglSetGammaTableParametersI3D = (PFNWGLSETGAMMATABLEPARAMETERSI3DPROC) load(userptr, "wglSetGammaTableParametersI3D");
}
static void glad_wgl_load_WGL_I3D_genlock(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_I3D_genlock) return;
glad_wglDisableGenlockI3D = (PFNWGLDISABLEGENLOCKI3DPROC) load(userptr, "wglDisableGenlockI3D");
glad_wglEnableGenlockI3D = (PFNWGLENABLEGENLOCKI3DPROC) load(userptr, "wglEnableGenlockI3D");
glad_wglGenlockSampleRateI3D = (PFNWGLGENLOCKSAMPLERATEI3DPROC) load(userptr, "wglGenlockSampleRateI3D");
glad_wglGenlockSourceDelayI3D = (PFNWGLGENLOCKSOURCEDELAYI3DPROC) load(userptr, "wglGenlockSourceDelayI3D");
glad_wglGenlockSourceEdgeI3D = (PFNWGLGENLOCKSOURCEEDGEI3DPROC) load(userptr, "wglGenlockSourceEdgeI3D");
glad_wglGenlockSourceI3D = (PFNWGLGENLOCKSOURCEI3DPROC) load(userptr, "wglGenlockSourceI3D");
glad_wglGetGenlockSampleRateI3D = (PFNWGLGETGENLOCKSAMPLERATEI3DPROC) load(userptr, "wglGetGenlockSampleRateI3D");
glad_wglGetGenlockSourceDelayI3D = (PFNWGLGETGENLOCKSOURCEDELAYI3DPROC) load(userptr, "wglGetGenlockSourceDelayI3D");
glad_wglGetGenlockSourceEdgeI3D = (PFNWGLGETGENLOCKSOURCEEDGEI3DPROC) load(userptr, "wglGetGenlockSourceEdgeI3D");
glad_wglGetGenlockSourceI3D = (PFNWGLGETGENLOCKSOURCEI3DPROC) load(userptr, "wglGetGenlockSourceI3D");
glad_wglIsEnabledGenlockI3D = (PFNWGLISENABLEDGENLOCKI3DPROC) load(userptr, "wglIsEnabledGenlockI3D");
glad_wglQueryGenlockMaxSourceDelayI3D = (PFNWGLQUERYGENLOCKMAXSOURCEDELAYI3DPROC) load(userptr, "wglQueryGenlockMaxSourceDelayI3D");
}
static void glad_wgl_load_WGL_I3D_image_buffer(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_I3D_image_buffer) return;
glad_wglAssociateImageBufferEventsI3D = (PFNWGLASSOCIATEIMAGEBUFFEREVENTSI3DPROC) load(userptr, "wglAssociateImageBufferEventsI3D");
glad_wglCreateImageBufferI3D = (PFNWGLCREATEIMAGEBUFFERI3DPROC) load(userptr, "wglCreateImageBufferI3D");
glad_wglDestroyImageBufferI3D = (PFNWGLDESTROYIMAGEBUFFERI3DPROC) load(userptr, "wglDestroyImageBufferI3D");
glad_wglReleaseImageBufferEventsI3D = (PFNWGLRELEASEIMAGEBUFFEREVENTSI3DPROC) load(userptr, "wglReleaseImageBufferEventsI3D");
}
static void glad_wgl_load_WGL_I3D_swap_frame_lock(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_I3D_swap_frame_lock) return;
glad_wglDisableFrameLockI3D = (PFNWGLDISABLEFRAMELOCKI3DPROC) load(userptr, "wglDisableFrameLockI3D");
glad_wglEnableFrameLockI3D = (PFNWGLENABLEFRAMELOCKI3DPROC) load(userptr, "wglEnableFrameLockI3D");
glad_wglIsEnabledFrameLockI3D = (PFNWGLISENABLEDFRAMELOCKI3DPROC) load(userptr, "wglIsEnabledFrameLockI3D");
glad_wglQueryFrameLockMasterI3D = (PFNWGLQUERYFRAMELOCKMASTERI3DPROC) load(userptr, "wglQueryFrameLockMasterI3D");
}
static void glad_wgl_load_WGL_I3D_swap_frame_usage(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_I3D_swap_frame_usage) return;
glad_wglBeginFrameTrackingI3D = (PFNWGLBEGINFRAMETRACKINGI3DPROC) load(userptr, "wglBeginFrameTrackingI3D");
glad_wglEndFrameTrackingI3D = (PFNWGLENDFRAMETRACKINGI3DPROC) load(userptr, "wglEndFrameTrackingI3D");
glad_wglGetFrameUsageI3D = (PFNWGLGETFRAMEUSAGEI3DPROC) load(userptr, "wglGetFrameUsageI3D");
glad_wglQueryFrameTrackingI3D = (PFNWGLQUERYFRAMETRACKINGI3DPROC) load(userptr, "wglQueryFrameTrackingI3D");
}
static void glad_wgl_load_WGL_NV_DX_interop(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_NV_DX_interop) return;
glad_wglDXCloseDeviceNV = (PFNWGLDXCLOSEDEVICENVPROC) load(userptr, "wglDXCloseDeviceNV");
glad_wglDXLockObjectsNV = (PFNWGLDXLOCKOBJECTSNVPROC) load(userptr, "wglDXLockObjectsNV");
glad_wglDXObjectAccessNV = (PFNWGLDXOBJECTACCESSNVPROC) load(userptr, "wglDXObjectAccessNV");
glad_wglDXOpenDeviceNV = (PFNWGLDXOPENDEVICENVPROC) load(userptr, "wglDXOpenDeviceNV");
glad_wglDXRegisterObjectNV = (PFNWGLDXREGISTEROBJECTNVPROC) load(userptr, "wglDXRegisterObjectNV");
glad_wglDXSetResourceShareHandleNV = (PFNWGLDXSETRESOURCESHAREHANDLENVPROC) load(userptr, "wglDXSetResourceShareHandleNV");
glad_wglDXUnlockObjectsNV = (PFNWGLDXUNLOCKOBJECTSNVPROC) load(userptr, "wglDXUnlockObjectsNV");
glad_wglDXUnregisterObjectNV = (PFNWGLDXUNREGISTEROBJECTNVPROC) load(userptr, "wglDXUnregisterObjectNV");
}
static void glad_wgl_load_WGL_NV_copy_image(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_NV_copy_image) return;
glad_wglCopyImageSubDataNV = (PFNWGLCOPYIMAGESUBDATANVPROC) load(userptr, "wglCopyImageSubDataNV");
}
static void glad_wgl_load_WGL_NV_delay_before_swap(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_NV_delay_before_swap) return;
glad_wglDelayBeforeSwapNV = (PFNWGLDELAYBEFORESWAPNVPROC) load(userptr, "wglDelayBeforeSwapNV");
}
static void glad_wgl_load_WGL_NV_gpu_affinity(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_NV_gpu_affinity) return;
glad_wglCreateAffinityDCNV = (PFNWGLCREATEAFFINITYDCNVPROC) load(userptr, "wglCreateAffinityDCNV");
glad_wglDeleteDCNV = (PFNWGLDELETEDCNVPROC) load(userptr, "wglDeleteDCNV");
glad_wglEnumGpuDevicesNV = (PFNWGLENUMGPUDEVICESNVPROC) load(userptr, "wglEnumGpuDevicesNV");
glad_wglEnumGpusFromAffinityDCNV = (PFNWGLENUMGPUSFROMAFFINITYDCNVPROC) load(userptr, "wglEnumGpusFromAffinityDCNV");
glad_wglEnumGpusNV = (PFNWGLENUMGPUSNVPROC) load(userptr, "wglEnumGpusNV");
}
static void glad_wgl_load_WGL_NV_present_video(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_NV_present_video) return;
glad_wglBindVideoDeviceNV = (PFNWGLBINDVIDEODEVICENVPROC) load(userptr, "wglBindVideoDeviceNV");
glad_wglEnumerateVideoDevicesNV = (PFNWGLENUMERATEVIDEODEVICESNVPROC) load(userptr, "wglEnumerateVideoDevicesNV");
glad_wglQueryCurrentContextNV = (PFNWGLQUERYCURRENTCONTEXTNVPROC) load(userptr, "wglQueryCurrentContextNV");
}
static void glad_wgl_load_WGL_NV_swap_group(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_NV_swap_group) return;
glad_wglBindSwapBarrierNV = (PFNWGLBINDSWAPBARRIERNVPROC) load(userptr, "wglBindSwapBarrierNV");
glad_wglJoinSwapGroupNV = (PFNWGLJOINSWAPGROUPNVPROC) load(userptr, "wglJoinSwapGroupNV");
glad_wglQueryFrameCountNV = (PFNWGLQUERYFRAMECOUNTNVPROC) load(userptr, "wglQueryFrameCountNV");
glad_wglQueryMaxSwapGroupsNV = (PFNWGLQUERYMAXSWAPGROUPSNVPROC) load(userptr, "wglQueryMaxSwapGroupsNV");
glad_wglQuerySwapGroupNV = (PFNWGLQUERYSWAPGROUPNVPROC) load(userptr, "wglQuerySwapGroupNV");
glad_wglResetFrameCountNV = (PFNWGLRESETFRAMECOUNTNVPROC) load(userptr, "wglResetFrameCountNV");
}
static void glad_wgl_load_WGL_NV_vertex_array_range(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_NV_vertex_array_range) return;
glad_wglAllocateMemoryNV = (PFNWGLALLOCATEMEMORYNVPROC) load(userptr, "wglAllocateMemoryNV");
glad_wglFreeMemoryNV = (PFNWGLFREEMEMORYNVPROC) load(userptr, "wglFreeMemoryNV");
}
static void glad_wgl_load_WGL_NV_video_capture(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_NV_video_capture) return;
glad_wglBindVideoCaptureDeviceNV = (PFNWGLBINDVIDEOCAPTUREDEVICENVPROC) load(userptr, "wglBindVideoCaptureDeviceNV");
glad_wglEnumerateVideoCaptureDevicesNV = (PFNWGLENUMERATEVIDEOCAPTUREDEVICESNVPROC) load(userptr, "wglEnumerateVideoCaptureDevicesNV");
glad_wglLockVideoCaptureDeviceNV = (PFNWGLLOCKVIDEOCAPTUREDEVICENVPROC) load(userptr, "wglLockVideoCaptureDeviceNV");
glad_wglQueryVideoCaptureDeviceNV = (PFNWGLQUERYVIDEOCAPTUREDEVICENVPROC) load(userptr, "wglQueryVideoCaptureDeviceNV");
glad_wglReleaseVideoCaptureDeviceNV = (PFNWGLRELEASEVIDEOCAPTUREDEVICENVPROC) load(userptr, "wglReleaseVideoCaptureDeviceNV");
}
static void glad_wgl_load_WGL_NV_video_output(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_NV_video_output) return;
glad_wglBindVideoImageNV = (PFNWGLBINDVIDEOIMAGENVPROC) load(userptr, "wglBindVideoImageNV");
glad_wglGetVideoDeviceNV = (PFNWGLGETVIDEODEVICENVPROC) load(userptr, "wglGetVideoDeviceNV");
glad_wglGetVideoInfoNV = (PFNWGLGETVIDEOINFONVPROC) load(userptr, "wglGetVideoInfoNV");
glad_wglReleaseVideoDeviceNV = (PFNWGLRELEASEVIDEODEVICENVPROC) load(userptr, "wglReleaseVideoDeviceNV");
glad_wglReleaseVideoImageNV = (PFNWGLRELEASEVIDEOIMAGENVPROC) load(userptr, "wglReleaseVideoImageNV");
glad_wglSendPbufferToVideoNV = (PFNWGLSENDPBUFFERTOVIDEONVPROC) load(userptr, "wglSendPbufferToVideoNV");
}
static void glad_wgl_load_WGL_OML_sync_control(GLADuserptrloadfunc load, void *userptr) {
if(!GLAD_WGL_OML_sync_control) return;
glad_wglGetMscRateOML = (PFNWGLGETMSCRATEOMLPROC) load(userptr, "wglGetMscRateOML");
glad_wglGetSyncValuesOML = (PFNWGLGETSYNCVALUESOMLPROC) load(userptr, "wglGetSyncValuesOML");
glad_wglSwapBuffersMscOML = (PFNWGLSWAPBUFFERSMSCOMLPROC) load(userptr, "wglSwapBuffersMscOML");
glad_wglSwapLayerBuffersMscOML = (PFNWGLSWAPLAYERBUFFERSMSCOMLPROC) load(userptr, "wglSwapLayerBuffersMscOML");
glad_wglWaitForMscOML = (PFNWGLWAITFORMSCOMLPROC) load(userptr, "wglWaitForMscOML");
glad_wglWaitForSbcOML = (PFNWGLWAITFORSBCOMLPROC) load(userptr, "wglWaitForSbcOML");
}
static void glad_wgl_resolve_aliases(void) {
}
static int glad_wgl_has_extension(HDC hdc, const char *ext) {
const char *terminator;
const char *loc;
const char *extensions;
if(wglGetExtensionsStringEXT == NULL && wglGetExtensionsStringARB == NULL)
return 0;
if(wglGetExtensionsStringARB == NULL || hdc == INVALID_HANDLE_VALUE)
extensions = wglGetExtensionsStringEXT();
else
extensions = wglGetExtensionsStringARB(hdc);
if(extensions == NULL || ext == NULL)
return 0;
while(1) {
loc = strstr(extensions, ext);
if(loc == NULL)
break;
terminator = loc + strlen(ext);
if((loc == extensions || *(loc - 1) == ' ') &&
(*terminator == ' ' || *terminator == '\0'))
{
return 1;
}
extensions = terminator;
}
return 0;
}
static GLADapiproc glad_wgl_get_proc_from_userptr(void *userptr, const char* name) {
return (GLAD_GNUC_EXTENSION (GLADapiproc (*)(const char *name)) userptr)(name);
}
static int glad_wgl_find_extensions_wgl(HDC hdc) {
GLAD_WGL_3DFX_multisample = glad_wgl_has_extension(hdc, "WGL_3DFX_multisample");
GLAD_WGL_3DL_stereo_control = glad_wgl_has_extension(hdc, "WGL_3DL_stereo_control");
GLAD_WGL_AMD_gpu_association = glad_wgl_has_extension(hdc, "WGL_AMD_gpu_association");
GLAD_WGL_ARB_buffer_region = glad_wgl_has_extension(hdc, "WGL_ARB_buffer_region");
GLAD_WGL_ARB_context_flush_control = glad_wgl_has_extension(hdc, "WGL_ARB_context_flush_control");
GLAD_WGL_ARB_create_context = glad_wgl_has_extension(hdc, "WGL_ARB_create_context");
GLAD_WGL_ARB_create_context_no_error = glad_wgl_has_extension(hdc, "WGL_ARB_create_context_no_error");
GLAD_WGL_ARB_create_context_profile = glad_wgl_has_extension(hdc, "WGL_ARB_create_context_profile");
GLAD_WGL_ARB_create_context_robustness = glad_wgl_has_extension(hdc, "WGL_ARB_create_context_robustness");
GLAD_WGL_ARB_extensions_string = glad_wgl_has_extension(hdc, "WGL_ARB_extensions_string");
GLAD_WGL_ARB_framebuffer_sRGB = glad_wgl_has_extension(hdc, "WGL_ARB_framebuffer_sRGB");
GLAD_WGL_ARB_make_current_read = glad_wgl_has_extension(hdc, "WGL_ARB_make_current_read");
GLAD_WGL_ARB_multisample = glad_wgl_has_extension(hdc, "WGL_ARB_multisample");
GLAD_WGL_ARB_pbuffer = glad_wgl_has_extension(hdc, "WGL_ARB_pbuffer");
GLAD_WGL_ARB_pixel_format = glad_wgl_has_extension(hdc, "WGL_ARB_pixel_format");
GLAD_WGL_ARB_pixel_format_float = glad_wgl_has_extension(hdc, "WGL_ARB_pixel_format_float");
GLAD_WGL_ARB_render_texture = glad_wgl_has_extension(hdc, "WGL_ARB_render_texture");
GLAD_WGL_ARB_robustness_application_isolation = glad_wgl_has_extension(hdc, "WGL_ARB_robustness_application_isolation");
GLAD_WGL_ARB_robustness_share_group_isolation = glad_wgl_has_extension(hdc, "WGL_ARB_robustness_share_group_isolation");
GLAD_WGL_ATI_pixel_format_float = glad_wgl_has_extension(hdc, "WGL_ATI_pixel_format_float");
GLAD_WGL_ATI_render_texture_rectangle = glad_wgl_has_extension(hdc, "WGL_ATI_render_texture_rectangle");
GLAD_WGL_EXT_colorspace = glad_wgl_has_extension(hdc, "WGL_EXT_colorspace");
GLAD_WGL_EXT_create_context_es2_profile = glad_wgl_has_extension(hdc, "WGL_EXT_create_context_es2_profile");
GLAD_WGL_EXT_create_context_es_profile = glad_wgl_has_extension(hdc, "WGL_EXT_create_context_es_profile");
GLAD_WGL_EXT_depth_float = glad_wgl_has_extension(hdc, "WGL_EXT_depth_float");
GLAD_WGL_EXT_display_color_table = glad_wgl_has_extension(hdc, "WGL_EXT_display_color_table");
GLAD_WGL_EXT_extensions_string = glad_wgl_has_extension(hdc, "WGL_EXT_extensions_string");
GLAD_WGL_EXT_framebuffer_sRGB = glad_wgl_has_extension(hdc, "WGL_EXT_framebuffer_sRGB");
GLAD_WGL_EXT_make_current_read = glad_wgl_has_extension(hdc, "WGL_EXT_make_current_read");
GLAD_WGL_EXT_multisample = glad_wgl_has_extension(hdc, "WGL_EXT_multisample");
GLAD_WGL_EXT_pbuffer = glad_wgl_has_extension(hdc, "WGL_EXT_pbuffer");
GLAD_WGL_EXT_pixel_format = glad_wgl_has_extension(hdc, "WGL_EXT_pixel_format");
GLAD_WGL_EXT_pixel_format_packed_float = glad_wgl_has_extension(hdc, "WGL_EXT_pixel_format_packed_float");
GLAD_WGL_EXT_swap_control = glad_wgl_has_extension(hdc, "WGL_EXT_swap_control");
GLAD_WGL_EXT_swap_control_tear = glad_wgl_has_extension(hdc, "WGL_EXT_swap_control_tear");
GLAD_WGL_I3D_digital_video_control = glad_wgl_has_extension(hdc, "WGL_I3D_digital_video_control");
GLAD_WGL_I3D_gamma = glad_wgl_has_extension(hdc, "WGL_I3D_gamma");
GLAD_WGL_I3D_genlock = glad_wgl_has_extension(hdc, "WGL_I3D_genlock");
GLAD_WGL_I3D_image_buffer = glad_wgl_has_extension(hdc, "WGL_I3D_image_buffer");
GLAD_WGL_I3D_swap_frame_lock = glad_wgl_has_extension(hdc, "WGL_I3D_swap_frame_lock");
GLAD_WGL_I3D_swap_frame_usage = glad_wgl_has_extension(hdc, "WGL_I3D_swap_frame_usage");
GLAD_WGL_NV_DX_interop = glad_wgl_has_extension(hdc, "WGL_NV_DX_interop");
GLAD_WGL_NV_DX_interop2 = glad_wgl_has_extension(hdc, "WGL_NV_DX_interop2");
GLAD_WGL_NV_copy_image = glad_wgl_has_extension(hdc, "WGL_NV_copy_image");
GLAD_WGL_NV_delay_before_swap = glad_wgl_has_extension(hdc, "WGL_NV_delay_before_swap");
GLAD_WGL_NV_float_buffer = glad_wgl_has_extension(hdc, "WGL_NV_float_buffer");
GLAD_WGL_NV_gpu_affinity = glad_wgl_has_extension(hdc, "WGL_NV_gpu_affinity");
GLAD_WGL_NV_multigpu_context = glad_wgl_has_extension(hdc, "WGL_NV_multigpu_context");
GLAD_WGL_NV_multisample_coverage = glad_wgl_has_extension(hdc, "WGL_NV_multisample_coverage");
GLAD_WGL_NV_present_video = glad_wgl_has_extension(hdc, "WGL_NV_present_video");
GLAD_WGL_NV_render_depth_texture = glad_wgl_has_extension(hdc, "WGL_NV_render_depth_texture");
GLAD_WGL_NV_render_texture_rectangle = glad_wgl_has_extension(hdc, "WGL_NV_render_texture_rectangle");
GLAD_WGL_NV_swap_group = glad_wgl_has_extension(hdc, "WGL_NV_swap_group");
GLAD_WGL_NV_vertex_array_range = glad_wgl_has_extension(hdc, "WGL_NV_vertex_array_range");
GLAD_WGL_NV_video_capture = glad_wgl_has_extension(hdc, "WGL_NV_video_capture");
GLAD_WGL_NV_video_output = glad_wgl_has_extension(hdc, "WGL_NV_video_output");
GLAD_WGL_OML_sync_control = glad_wgl_has_extension(hdc, "WGL_OML_sync_control");
return 1;
}
static int glad_wgl_find_core_wgl(void) {
int major = 1, minor = 0;
GLAD_WGL_VERSION_1_0 = (major == 1 && minor >= 0) || major > 1;
return GLAD_MAKE_VERSION(major, minor);
}
int gladLoadWGLUserPtr(HDC hdc, GLADuserptrloadfunc load, void *userptr) {
int version;
wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC) load(userptr, "wglGetExtensionsStringARB");
wglGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC) load(userptr, "wglGetExtensionsStringEXT");
if(wglGetExtensionsStringARB == NULL && wglGetExtensionsStringEXT == NULL) return 0;
version = glad_wgl_find_core_wgl();
if (!glad_wgl_find_extensions_wgl(hdc)) return 0;
glad_wgl_load_WGL_3DL_stereo_control(load, userptr);
glad_wgl_load_WGL_AMD_gpu_association(load, userptr);
glad_wgl_load_WGL_ARB_buffer_region(load, userptr);
glad_wgl_load_WGL_ARB_create_context(load, userptr);
glad_wgl_load_WGL_ARB_extensions_string(load, userptr);
glad_wgl_load_WGL_ARB_make_current_read(load, userptr);
glad_wgl_load_WGL_ARB_pbuffer(load, userptr);
glad_wgl_load_WGL_ARB_pixel_format(load, userptr);
glad_wgl_load_WGL_ARB_render_texture(load, userptr);
glad_wgl_load_WGL_EXT_display_color_table(load, userptr);
glad_wgl_load_WGL_EXT_extensions_string(load, userptr);
glad_wgl_load_WGL_EXT_make_current_read(load, userptr);
glad_wgl_load_WGL_EXT_pbuffer(load, userptr);
glad_wgl_load_WGL_EXT_pixel_format(load, userptr);
glad_wgl_load_WGL_EXT_swap_control(load, userptr);
glad_wgl_load_WGL_I3D_digital_video_control(load, userptr);
glad_wgl_load_WGL_I3D_gamma(load, userptr);
glad_wgl_load_WGL_I3D_genlock(load, userptr);
glad_wgl_load_WGL_I3D_image_buffer(load, userptr);
glad_wgl_load_WGL_I3D_swap_frame_lock(load, userptr);
glad_wgl_load_WGL_I3D_swap_frame_usage(load, userptr);
glad_wgl_load_WGL_NV_DX_interop(load, userptr);
glad_wgl_load_WGL_NV_copy_image(load, userptr);
glad_wgl_load_WGL_NV_delay_before_swap(load, userptr);
glad_wgl_load_WGL_NV_gpu_affinity(load, userptr);
glad_wgl_load_WGL_NV_present_video(load, userptr);
glad_wgl_load_WGL_NV_swap_group(load, userptr);
glad_wgl_load_WGL_NV_vertex_array_range(load, userptr);
glad_wgl_load_WGL_NV_video_capture(load, userptr);
glad_wgl_load_WGL_NV_video_output(load, userptr);
glad_wgl_load_WGL_OML_sync_control(load, userptr);
glad_wgl_resolve_aliases();
return version;
}
int gladLoadWGL(HDC hdc, GLADloadfunc load) {
return gladLoadWGLUserPtr(hdc, glad_wgl_get_proc_from_userptr, GLAD_GNUC_EXTENSION (void*) load);
}
#ifdef GLAD_WGL
static GLADapiproc glad_wgl_get_proc(void *vuserptr, const char* name) {
GLAD_UNUSED(vuserptr);
return GLAD_GNUC_EXTENSION (GLADapiproc) wglGetProcAddress(name);
}
int gladLoaderLoadWGL(HDC hdc) {
return gladLoadWGLUserPtr(hdc, glad_wgl_get_proc, NULL);
}
#endif /* GLAD_WGL */
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,105 @@
#include "Base/Adorn.hpp"
namespace Aya
{
template<typename Modifier>
static void outlineRect2dImpl(Adorn* adorn, const Rect2D& rect, float thick, const Color4& color, const Modifier& modifier)
{
adorn->rect2d(Rect2D::xyxy(rect.x0() - thick, rect.y0() - thick, rect.x0(), rect.y1()), color, modifier);
adorn->rect2d(Rect2D::xyxy(rect.x1(), rect.y0(), rect.x1() + thick, rect.y1() + thick), color, modifier);
adorn->rect2d(Rect2D::xyxy(rect.x0(), rect.y0() - thick, rect.x1() + thick, rect.y0()), color, modifier);
adorn->rect2d(Rect2D::xyxy(rect.x0() - thick, rect.y1(), rect.x1(), rect.y1() + thick), color, modifier);
}
void Adorn::outlineRect2d(const Rect2D& rect, float thick, const Color4& color)
{
outlineRect2dImpl(this, rect, thick, color, Rotation2D());
}
void Adorn::outlineRect2d(const Rect2D& rect, float thick, const Color4& color, const Rotation2D& rotation)
{
outlineRect2dImpl(this, rect, thick, color, rotation);
}
void Adorn::outlineRect2d(const Rect2D& rect, float thick, const Color4& color, const Rect2D& clipRect)
{
outlineRect2dImpl(this, rect, thick, color, clipRect);
}
void Adorn::rect2d(const Rect2D& rect, const Color4& color)
{
rect2d(rect, Vector2(0, 0), Vector2(1, 1), color, Rotation2D());
}
void Adorn::rect2d(const Rect2D& rect, const Color4& color, const Rotation2D& rotation)
{
rect2d(rect, Vector2(0, 0), Vector2(1, 1), color, rotation);
}
void Adorn::rect2d(const Rect2D& rect, const Color4& color, const Rect2D& clipRect)
{
rect2d(rect, Vector2(0, 0), Vector2(1, 1), color, clipRect);
}
void Adorn::rect2d(const Rect2D& rect, const Vector2& texul, const Vector2& texbr, const Color4& color)
{
rect2d(rect, texul, texbr, color, Rotation2D());
}
void Adorn::rect2d(const Rect2D& rect, const Vector2& texul, const Vector2& texbr, const Color4& color, const Rotation2D& rotation)
{
if (!rotation.empty())
{
Vector2 x0y0 = rotation.rotate(rect.x0y0());
Vector2 x1y0 = rotation.rotate(rect.x1y0());
Vector2 x0y1 = rotation.rotate(rect.x0y1());
Vector2 x1y1 = rotation.rotate(rect.x1y1());
rect2dImpl(x0y0, x1y0, x0y1, x1y1, texul, texbr, color);
}
else
{
rect2dImpl(rect.x0y0(), rect.x1y0(), rect.x0y1(), rect.x1y1(), texul, texbr, color);
}
}
void Adorn::rect2d(const Rect2D& rect, const Vector2& texul, const Vector2& texbr, const Color4& color, const Rect2D& clipRect)
{
Aya::Rect2D intersectRect = rect;
Vector2 lowerUV(texul.x, texul.y);
Vector2 upperUV(texbr.x, texbr.y);
if (clipRect != rect)
{
intersectRect = clipRect.intersect(rect);
if (rect.width() != 0)
{
float uvwidth = upperUV.x - lowerUV.x;
lowerUV.x += (uvwidth * (intersectRect.x0() - rect.x0()) / rect.width());
upperUV.x += (uvwidth * (intersectRect.x1() - rect.x1()) / rect.width());
}
if (rect.height() != 0)
{
float uvheight = upperUV.y - lowerUV.y;
lowerUV.y += (uvheight * (intersectRect.y0() - rect.y0()) / rect.height());
upperUV.y += (uvheight * (intersectRect.y1() - rect.y1()) / rect.height());
}
}
rect2dImpl(intersectRect.x0y0(), intersectRect.x1y0(), intersectRect.x0y1(), intersectRect.x1y1(), lowerUV, upperUV, color);
}
Vector2 Adorn::drawFont2D(const std::string& s, const Vector2& position, float size, bool autoScale, const Color4& color, const Color4& outline,
Text::Font font, Text::XAlign xalign, Text::YAlign yalign, const Vector2& availableSpace, const Rect2D& clippingRect, const Rotation2D& rotation)
{
return drawFont2DImpl(this, s, position, size, autoScale, color, outline, font, xalign, yalign, availableSpace, clippingRect, rotation);
}
} // namespace Aya

View File

@@ -0,0 +1,274 @@
#pragma once
#include <string>
#include "Base/Type.hpp"
#include "Base/TextureProxyBase.hpp"
#include "Utility/Extents.hpp"
#include "Utility/G3DCore.hpp"
#include "Utility/ContentId.hpp"
#include "Utility/Rotation2d.hpp"
#include "signal.hpp"
#include "Declarations.hpp"
namespace Aya
{
class I3DLinearFunc;
class RenderCaps;
class Camera;
struct Canvas
{
Canvas(Vector2 viewPort)
: size(viewPort)
{
}
Vector2 size;
Vector2 toPixelSize(const Vector2& percent) const; // std screen is 100% wide and 75% tall
int normalizedFontSize(int fontSize) const;
};
// Aya::Adorn is a base class used to decorate other objects using 2D or 3D
// basic shapes.
class AyaBaseClass Adorn
{
public:
enum Material
{
Material_Default,
Material_NoLighting,
Material_SelfLit,
Material_SelfLitHighlight,
Material_AALine,
Material_Outline,
Material_Count
};
Adorn()
: ignoreTexture(false)
, currentMaterial(Material_Default)
{
}
Canvas getCanvas() const
{
return getViewport().wh();
}
virtual const Camera* getCamera() const = 0;
virtual ~Adorn() {}
virtual TextureProxyBaseRef createTextureProxy(const ContentId& id, bool& waiting, bool bBlocking = false, const std::string& context = "") = 0;
virtual TextureProxyBaseRef createTextureProxy(int width, int height) = 0;
// Listen to this signal if you need a hint about when to release your
// TextureProxys.
virtual Aya::signal<void()>& getUnbindResourcesSignal() = 0;
// Called to perform any preparations before the render pass begins.
virtual void prepareRenderPass() {}
// Called to perform any cleanup after every 2D/3D render pass.
virtual void finishRenderPass() {}
virtual void preSubmitPass() {}
virtual void postSubmitPass() {}
virtual bool useFontSmoothScalling()
{
return false;
}
void setMaterial(Material material_)
{
currentMaterial = material_;
}
Material getMaterial() const
{
return currentMaterial;
}
/////////////////////////////////////////////////
//
// Viewport
// Returns the Adorn's viewport area.
//
// Note that this viewport doesn't always represent the area where the
// game is being displayed.
virtual Rect2D getViewport() const = 0;
// Hack - buffering the GuiRect here - ultimately need to clip the
// "User Gui Space" with the Aya Gui stuff.
void setUserGuiInset(const Vector4& value)
{
userGuiInset = value;
}
Rect2D getUserGuiRect() const
{
Rect2D vp = getViewport();
return Rect2D::xyxy(vp.x0() + userGuiInset.x, vp.y0() + userGuiInset.y, vp.x1() - userGuiInset.z, vp.y1() - userGuiInset.w);
}
/////////////////////////////////////////////////
//
// Textures, Draw Rectangles, Lines
void setIgnoreTexture(bool ignore)
{
ignoreTexture = ignore;
}
bool getIgnoreTexture() const
{
return ignoreTexture;
}
// Sets the texture used by the Adorn.
virtual void setTexture(int id, const Aya::TextureProxyBaseRef& texture) = 0;
// Gets the size of the texture being used by the Adorn.
virtual Rect2D getTextureSize(const Aya::TextureProxyBaseRef& texture) const = 0;
// Draws a line on the screen.
virtual void line2d(const Vector2& p0, const Vector2& p1, const Color4& color) = 0;
// Draws a hollow rectangle on the screen.
void outlineRect2d(const Rect2D& rect, float thick, const Color4& color);
void outlineRect2d(const Rect2D& rect, float thick, const Color4& color, const Rotation2D& rotation);
void outlineRect2d(const Rect2D& rect, float thick, const Color4& color, const Rect2D& clipRect);
// Draws a solid rectangle on the screen.
void rect2d(const Rect2D& rect, const Color4& color);
void rect2d(const Rect2D& rect, const Color4& color, const Rotation2D& rotation);
void rect2d(const Rect2D& rect, const Color4& color, const Rect2D& clipRect);
void rect2d(const Rect2D& rect, const Vector2& texul, const Vector2& texbr, const Color4& color);
void rect2d(const Rect2D& rect, const Vector2& texul, const Vector2& texbr, const Color4& color, const Rotation2D& rotation);
void rect2d(const Rect2D& rect, const Vector2& texul, const Vector2& texbr, const Color4& color, const Rect2D& clipRect);
// Rectangle drawing implementation
virtual void rect2dImpl(const Vector2& x0y0, const Vector2& x1y0, const Vector2& x0y1, const Vector2& x1y1, const Vector2& tex0,
const Vector2& tex1, const Color4& color) = 0;
/////////////////////////////////////////////////
//
// Draw Fonts
// Retrieves the boundaries (in pixels) of a string, if it were to be
// drawn on screen.
virtual Vector2 get2DStringBounds(
const std::string& s, float size, Text::Font font = Text::FONT_LEGACY, const Vector2& availableSpace = Vector2::zero()) const = 0;
// Draws a string using this Adorn.
Vector2 drawFont2D(const std::string& s, const Vector2& position, float size, bool autoScale, const Color4& color = Color3::black(),
const Color4& outline = Color4::clear(), Text::Font font = Text::FONT_LEGACY, Text::XAlign xalign = Text::XALIGN_LEFT,
Text::YAlign yalign = Text::YALIGN_TOP, const Vector2& availableSpace = Vector2::zero(),
const Rect2D& clippingRect = Aya::Rect2D::xyxy(-1, -1, -1, -1), const Rotation2D& rotation = Rotation2D());
virtual Vector2 drawFont2DImpl(Adorn* target, const std::string& s, const Vector2& position, float size, bool autoScale, const Color4& color,
const Color4& outline, Text::Font font, Text::XAlign xalign, Text::YAlign yalign, const Vector2& availableSpace, const Rect2D& clippingRect,
const Rotation2D& rotation) = 0;
/////////////////////////////////////////////////
//
// 3D stuff - procedural
virtual void line3d(const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color) = 0;
virtual void line3dAA(
const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color, float thickness, int zIndex, bool alwaysOnTop) = 0;
// Sets the adorn's coordinate frame.
virtual void setObjectToWorldMatrix(const CoordinateFrame& c) = 0;
// Draws an axis aligned bounding box on the adorn.
virtual void box(const AABox& box, const Color4& solidColor = Color4(1, .2f, .2f, .5f)) = 0;
// Draws a box on the adorn.
void box(const Extents& extents, const Color4& solidColor = Color4(1, .2f, .2f, .5f))
{
AABox aaBox(extents.min(), extents.max());
box(aaBox, solidColor);
}
virtual void box(const CoordinateFrame& cFrame, const Vector3& size, const Color4& color, int zIndex, bool alwaysOnTop) = 0;
// Draws a sphere on the adorn.
virtual void sphere(const Sphere& sphere, const Color4& solidColor = Color4(1, 1, 0, .5f)) = 0;
virtual void sphere(const CoordinateFrame& cFrame, float radius, const Color4& color, int zIndex, bool alwaysOnTop) = 0;
// Draws an explosion on the adorn.
virtual void explosion(const Sphere& sphere) = 0;
virtual void cylinder(const CoordinateFrame& cFrame, float radius, float height, const Color4& color, int zIndex, bool alwaysOnTop) = 0;
// Draws a cylinder along the adorn's x axis.
virtual void cylinderAlongX(float radius, float length, const Color4& solidColor, bool cap = true) = 0;
virtual void cone(const CoordinateFrame& cFrame, float radius, float height, const Color4& color, int zIndex, bool alwaysOnTop) = 0;
// Draws a ray from the adorn.
virtual void ray(const RbxRay& ray, const Color4& color = Color3::orange()) = 0;
// Draws the x, y, z axis of the adorn.
virtual void axes(
const Color4& xColor = Color3::red(), const Color4& yColor = Color3::green(), const Color4& zColor = Color3::blue(), float scale = 1.0f) = 0;
// Draws a quad on the adorn.
//
// v0 The first point to form the quad.
// v1 The second point to form the quad.
// v2 The third point to form the quad.
// v3 The fourth point to form the quad.
// color The color used to draw the quad.
// v0tex UV coordinates used on the first polygon.
// v2tex UV coordinates used on the second polygon.
// opt The material options to use when drawing the quad.
virtual void quad(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Vector3& v3, const Color4& color = Color3::blue(),
const Vector2& v0tex = Vector2::zero(), const Vector2& v2tex = Vector2::zero(), int zIndex = -1, bool alwaysOnTop = false) = 0;
// Draws a convex 3D polygon on the adorn.
//
// v The set of points that compose the polygon.
// countv The number of points that compose the polygon.
// color The color used when drawing the polygon.
// opt The material options to use when drawing the polygon.
virtual void convexPolygon(const Vector3* v, int countv, const Color4& color) = 0;
// Draws a convex 2D polygon on the adorn.
//
// v The set of points that compose the polygon.
// countv The number of points that compose the polygon.
// color The color used when drawing the polygon.
// opt The material options to use when drawing the polygon.
virtual void convexPolygon2d(const Vector2* v, int countv, const Color4& color) = 0;
// Evaluates extrusion, calling trajectory and profile func with domain [0..1].
// if closeTrajectory or closeProfile is true, func is only evaluated to [0..1[,
// and evaluation for f(0) is used again for f(1).
// Future: if closeTrajectory != closeProfile, that could indicate we need
// caps at the end.
virtual void extrusion(Aya::I3DLinearFunc* trajectory, int trajectorysegments, Aya::I3DLinearFunc* profile, int profilesegments,
const Color4& color, bool closeTrajectory = true, bool closeProfile = true) = 0;
virtual bool isVisible(const Extents& extents, const CoordinateFrame& cframe)
{
return true;
}
static const int maximumZIndex = 10;
protected:
Vector4 userGuiInset;
bool ignoreTexture;
Material currentMaterial;
};
} // namespace Aya

View File

@@ -0,0 +1,72 @@
#include "Base/AdornBillboarder.hpp"
#include "Base/ViewportBillboarder.hpp"
#include "DataModel/Camera.hpp"
namespace Aya
{
AdornBillboarder::AdornBillboarder(Adorn* parent, const ViewportBillboarder& viewportBillboarder)
: parent(parent)
, viewport(viewportBillboarder.getViewport())
, alwaysOnTop(viewportBillboarder.alwaysOnTop)
{
parent->setObjectToWorldMatrix(viewportBillboarder.getCoordinateFrame());
}
AdornBillboarder::AdornBillboarder(Adorn* parent, const Rect2D& viewport, const CoordinateFrame& transform, bool alwaysOnTop)
: parent(parent)
, viewport(viewport)
, alwaysOnTop(alwaysOnTop)
{
parent->setObjectToWorldMatrix(transform);
}
Rect2D AdornBillboarder::getViewport() const
{
return viewport;
}
void AdornBillboarder::line2d(const Vector2& p0, const Vector2& p1, const Color4& color)
{
Vector3 p03D = Vector3(p0, 0);
Vector3 p13D = Vector3(p1, 0);
p03D.y *= -1;
p13D.y *= -1;
parent->line3d(p03D, p13D, color);
}
void AdornBillboarder::rect2dImpl(
const Vector2& x0y0, const Vector2& x1y0, const Vector2& x0y1, const Vector2& x1y1, const Vector2& tex0, const Vector2& tex1, const Color4& color)
{
Vector3 px0y0(x0y0, 0);
Vector3 px1y0(x1y0, 0);
Vector3 px0y1(x0y1, 0);
Vector3 px1y1(x1y1, 0);
px0y1.y *= -1;
px1y1.y *= -1;
px0y0.y *= -1;
px1y0.y *= -1;
parent->quad(px0y0, px1y0, px0y1, px1y1, color, tex0, tex1, 0, alwaysOnTop);
}
void AdornBillboarder::convexPolygon2d(const Vector2* v, int countv, const Color4& color)
{
#ifdef _WIN32
Vector3* v3d = (Vector3*)_alloca(sizeof(Vector3) * countv);
#else
Vector3* v3d = (Vector3*)alloca(sizeof(Vector3) * countv);
#endif
for (int i = 0; i < countv; ++i)
{
v3d[i] = Vector3(v[i].x, -v[i].y, 0); /*neg y : convert from ui space (0,0 top left) to math space */
}
parent->convexPolygon(v3d, countv, color);
}
} // namespace Aya

View File

@@ -0,0 +1,172 @@
#pragma once
#include "Base/Adorn.hpp"
namespace Aya
{
class ViewportBillboarder;
class AdornBillboarder : public Adorn
{
Adorn* parent;
Rect2D viewport;
bool alwaysOnTop;
public:
AdornBillboarder(Adorn* parent, const ViewportBillboarder& viewportBillboarder);
AdornBillboarder(Adorn* parent, const Rect2D& viewport, const CoordinateFrame& transform, bool alwaysOnTop = false);
/*override*/ TextureProxyBaseRef createTextureProxy(const ContentId& id, bool& waiting, bool bBlocking = false, const std::string& context = "")
{
return parent->createTextureProxy(id, waiting, bBlocking, context);
};
/*override*/ TextureProxyBaseRef createTextureProxy(int width, int height)
{
return parent->createTextureProxy(width, height);
};
/*override*/ Aya::signal<void()>& getUnbindResourcesSignal()
{
return parent->getUnbindResourcesSignal();
}
/////////////////////////////////////////////////
//
// Viewport
/*override*/ Rect2D getViewport() const;
virtual const Camera* getCamera() const
{
return NULL;
}
/////////////////////////////////////////////////
//
// Textures, Draw Rectangles, Lines
/*override*/ void setTexture(int id, const Aya::TextureProxyBaseRef& texture)
{
parent->setTexture(id, texture);
};
/*override*/ Rect2D getTextureSize(const Aya::TextureProxyBaseRef& texture) const
{
return parent->getTextureSize(texture);
};
virtual bool useFontSmoothScalling()
{
return true;
}
virtual void line2d(const Vector2& p0, const Vector2& p1, const Color4& color);
virtual void rect2dImpl(const Vector2& x0y0, const Vector2& x1y0, const Vector2& x0y1, const Vector2& x1y1, const Vector2& tex0,
const Vector2& tex1, const Color4& color);
virtual Vector2 get2DStringBounds(const std::string& s, float size, Text::Font font, const Vector2& availableSpace) const
{
return parent->get2DStringBounds(s, size, font, availableSpace);
}
virtual Vector2 drawFont2DImpl(Adorn* target, const std::string& s, const Vector2& position, float size, bool autoScale, const Color4& color,
const Color4& outline, Text::Font font, Text::XAlign xalign, Text::YAlign yalign, const Vector2& availableSpace, const Rect2D& clippingRect,
const Rotation2D& rotation)
{
return parent->drawFont2DImpl(
target, s, position, size, autoScale, color, outline, font, xalign, yalign, availableSpace, clippingRect, rotation);
}
/////////////////////////////////////////////////
//
// 3D stuff - procedural
/*override*/ void setObjectToWorldMatrix(const CoordinateFrame& c)
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void box(const AABox& box, const Color4& solidColor = Color4(1, .2f, .2f, .5f))
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void box(const CoordinateFrame& cFrame, const Vector3& size, const Color4& color, int zIndex, bool alwaysOnTop)
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void sphere(const Sphere& sphere, const Color4& solidColor = Color4(1, 1, 0, .5f))
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void sphere(const CoordinateFrame& cFrame, float radius, const Color4& color, int zIndex, bool alwaysOnTop)
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void explosion(const Sphere& sphere)
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void cylinder(const CoordinateFrame& cFrame, float radius, float height, const Color4& color, int zIndex, bool alwaysOnTop)
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void cylinderAlongX(float radius, float length, const Color4& solidColor, bool cap = true)
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void cone(const CoordinateFrame& cFrame, float radius, float height, const Color4& color, int zIndex, bool alwaysOnTop)
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void ray(const RbxRay& ray, const Color4& color = Color3::orange())
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void line3d(const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color)
{
throw std::runtime_error("Invalid operation");
};
/*override*/ virtual void line3dAA(
const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color, float thickness, int zIndex, bool alwaysOnTop)
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void axes(
const Color4& xColor = Color3::red(), const Color4& yColor = Color3::green(), const Color4& zColor = Color3::blue(), float scale = 1.0f)
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void quad(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Vector3& v3, const Color4& color = Color3::blue(),
const Vector2& v0tex = Vector2::zero(), const Vector2& v2tex = Vector2::zero(), int zIndex = -1, bool alwaysOnTop = false)
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void convexPolygon2d(const Vector2* v, int countv, const Color4& color);
/*override*/ void convexPolygon(const Vector3* v, int countv, const Color4& color)
{
throw std::runtime_error("Invalid operation");
};
// evaluates extrusion, calling trajectory and profile func with domain [0..1].
// if closeTrajectory or closeProfile is true, func is only evaluated to [0..1[, and evaluation for f(0) is used again for f(1).
// future: if closeTrajectory != closeProfile, that could indicate we need caps at the end.
/*override*/ void extrusion(I3DLinearFunc* trajectory, int trajectorysegments, I3DLinearFunc* profile, int profilesegments, const Color4& color,
bool closeTrajectory = true, bool closeProfile = true)
{
throw std::runtime_error("Invalid operation");
};
};
}; // namespace Aya

View File

@@ -0,0 +1,29 @@
#include "Base/AdornBillboarder2D.hpp"
namespace Aya
{
AdornBillboarder2D::AdornBillboarder2D(Adorn* parent, const Rect2D& viewport, const Vector2& screenOffset)
: parent(parent)
, viewport(viewport)
, screenOffset(screenOffset)
{
}
Rect2D AdornBillboarder2D::getViewport() const
{
return viewport;
}
void AdornBillboarder2D::line2d(const Vector2& p0, const Vector2& p1, const Color4& color)
{
parent->line2d(p0 + screenOffset, p1 + screenOffset, color);
}
void AdornBillboarder2D::rect2dImpl(
const Vector2& x0y0, const Vector2& x1y0, const Vector2& x0y1, const Vector2& x1y1, const Vector2& tex0, const Vector2& tex1, const Color4& color)
{
parent->rect2dImpl(x0y0 + screenOffset, x1y0 + screenOffset, x0y1 + screenOffset, x1y1 + screenOffset, tex0, tex1, color);
}
} // namespace Aya

View File

@@ -0,0 +1,174 @@
#pragma once
#include "Base/Adorn.hpp"
namespace Aya
{
class AdornBillboarder2D : public Adorn
{
protected:
Adorn* parent;
Rect2D viewport;
Vector2 screenOffset;
public:
AdornBillboarder2D(Adorn* parent, const Rect2D& viewport, const Vector2& screenOffset);
/*override*/ TextureProxyBaseRef createTextureProxy(const ContentId& id, bool& waiting, bool bBlocking = false, const std::string& context = "")
{
return parent->createTextureProxy(id, waiting, bBlocking, context);
};
/*override*/ TextureProxyBaseRef createTextureProxy(int width, int height)
{
return parent->createTextureProxy(width, height);
};
/*override*/ Aya::signal<void()>& getUnbindResourcesSignal()
{
return parent->getUnbindResourcesSignal();
}
/////////////////////////////////////////////////
//
// Viewport
/*override*/ Rect2D getViewport() const;
virtual const Camera* getCamera() const
{
return NULL;
}
/////////////////////////////////////////////////
//
// Textures, Draw Rectangles, Lines
/*override*/ void setTexture(int id, const Aya::TextureProxyBaseRef& texture)
{
parent->setTexture(id, texture);
};
/*override*/ Rect2D getTextureSize(const Aya::TextureProxyBaseRef& texture) const
{
return parent->getTextureSize(texture);
};
virtual bool useFontSmoothScalling()
{
return true;
}
virtual void line2d(const Vector2& p0, const Vector2& p1, const Color4& color);
virtual void rect2dImpl(const Vector2& x0y0, const Vector2& x1y0, const Vector2& x0y1, const Vector2& x1y1, const Vector2& tex0,
const Vector2& tex1, const Color4& color);
virtual Vector2 get2DStringBounds(const std::string& s, float size, Text::Font font, const Vector2& availableSpace) const
{
return parent->get2DStringBounds(s, size, font, availableSpace);
}
virtual Vector2 drawFont2DImpl(Adorn* target, const std::string& s, const Vector2& position, float size, bool autoScale, const Color4& color,
const Color4& outline, Text::Font font, Text::XAlign xalign, Text::YAlign yalign, const Vector2& availableSpace, const Rect2D& clippingRect,
const Rotation2D& rotation)
{
return parent->drawFont2DImpl(
target, s, position, size, autoScale, color, outline, font, xalign, yalign, availableSpace, clippingRect, rotation);
}
/////////////////////////////////////////////////
//
// 3D stuff - procedural
/*override*/ void setObjectToWorldMatrix(const CoordinateFrame& c)
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void box(const AABox& box, const Color4& solidColor = Color4(1, .2f, .2f, .5f))
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void box(const CoordinateFrame& cFrame, const Vector3& size, const Color4& color, int zIndex, bool alwaysOnTop)
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void sphere(const Sphere& sphere, const Color4& solidColor = Color4(1, 1, 0, .5f))
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void sphere(const CoordinateFrame& cFrame, float radius, const Color4& color, int zIndex, bool drawFront)
{
throw std::runtime_error("Invalid ooperation");
};
/*override*/ void explosion(const Sphere& sphere)
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void cylinder(const CoordinateFrame& cFrame, float radius, float height, const Color4& color, int zIndex, bool alwaysOnTop)
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void cylinderAlongX(float radius, float length, const Color4& solidColor, bool cap = true)
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void cone(const CoordinateFrame& cFrame, float radius, float height, const Color4& color, int zIndex, bool alwaysOnTop)
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void ray(const RbxRay& ray, const Color4& color = Color3::orange())
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void line3d(const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color)
{
throw std::runtime_error("Invalid operation");
};
/*override*/ virtual void line3dAA(
const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color, float thickness, int zIndex, bool alwaysOnTop)
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void axes(
const Color4& xColor = Color3::red(), const Color4& yColor = Color3::green(), const Color4& zColor = Color3::blue(), float scale = 1.0f)
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void quad(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Vector3& v3, const Color4& color = Color3::blue(),
const Vector2& v0tex = Vector2::zero(), const Vector2& v2tex = Vector2::zero(), int zIndex = -1, bool alwaysOnTop = false)
{
throw std::runtime_error("Invalid operation");
};
/*override*/ void convexPolygon2d(const Vector2* v, int countv, const Color4& color)
{
throw std::runtime_error("Invalid operation");
}
/*override*/ void convexPolygon(const Vector3* v, int countv, const Color4& color)
{
throw std::runtime_error("Invalid operation");
};
// evaluates extrusion, calling trajectory and profile func with domain [0..1].
// if closeTrajectory or closeProfile is true, func is only evaluated to [0..1[, and evaluation for f(0) is used again for f(1).
// future: if closeTrajectory != closeProfile, that could indicate we need caps at the end.
/*override*/ void extrusion(I3DLinearFunc* trajectory, int trajectorysegments, I3DLinearFunc* profile, int profilesegments, const Color4& color,
bool closeTrajectory = true, bool closeProfile = true)
{
throw std::runtime_error("Invalid operation");
};
};
}; // namespace Aya

View File

@@ -0,0 +1,69 @@
#include "Base/AdornSurface.hpp"
namespace Aya
{
AdornSurface::AdornSurface(Adorn* parent, const Rect2D& viewport, const CoordinateFrame& transform, bool alwaysOnTop)
: parent(parent)
, viewport(viewport)
, alwaysOnTop(alwaysOnTop)
{
parent->setObjectToWorldMatrix(transform);
}
Rect2D AdornSurface::getViewport() const
{
return viewport;
}
//////////////////////////////////////////////////////////////////////////
void AdornSurface::setTexture(int id, const Aya::TextureProxyBaseRef& t)
{
return parent->setTexture(id, t);
}
Rect2D AdornSurface::getTextureSize(const Aya::TextureProxyBaseRef& texture) const
{
return parent->getTextureSize(texture);
}
void AdornSurface::line2d(const Vector2& p0, const Vector2& p1, const Color4& color)
{
Vector3 p03D = Vector3(p0, 0);
Vector3 p13D = Vector3(p1, 0);
p03D.y *= -1;
p13D.y *= -1;
parent->line3d(p03D, p13D, color);
}
void AdornSurface::rect2dImpl(
const Vector2& x0y0, const Vector2& x1y0, const Vector2& x0y1, const Vector2& x1y1, const Vector2& tex0, const Vector2& tex1, const Color4& color)
{
Vector3 px0y0(x0y0, 0);
Vector3 px1y0(x1y0, 0);
Vector3 px0y1(x0y1, 0);
Vector3 px1y1(x1y1, 0);
px0y1.y *= -1;
px1y1.y *= -1;
px0y0.y *= -1;
px1y0.y *= -1;
parent->quad(px0y0, px1y0, px0y1, px1y1, color, tex0, tex1, 0, alwaysOnTop);
}
Vector2 AdornSurface::drawFont2DImpl(Adorn* target, const std::string& s, const Vector2& pos2D, float size, bool autoScale, const Color4& color,
const Color4& outline, Text::Font font, Text::XAlign xalign, Text::YAlign yalign, const Vector2& availableSpace, const Rect2D& clippingRect,
const Rotation2D& rotation)
{
return parent->drawFont2DImpl(target, s, pos2D, size, autoScale, color, outline, font, xalign, yalign, availableSpace, clippingRect, rotation);
}
Vector2 AdornSurface::get2DStringBounds(const std::string& s, float size, Text::Font font, const Vector2& availableSpace) const
{
return parent->get2DStringBounds(s, size, font, availableSpace);
}
} // namespace Aya

View File

@@ -0,0 +1,127 @@
#pragma once
#include "Base/Adorn.hpp"
#include "DataModel/Workspace.hpp"
#include "Utility/UDim.hpp"
namespace Aya
{
class AdornSurface : public Adorn
{
Adorn* parent;
Rect2D viewport;
bool alwaysOnTop;
public:
AdornSurface(Adorn* parent, const Rect2D& viewport, const CoordinateFrame& transform, bool alwaysOnTop = false);
virtual bool useFontSmoothScalling()
{
return false;
}
void setTexture(int id, const Aya::TextureProxyBaseRef& texture);
Rect2D getTextureSize(const Aya::TextureProxyBaseRef& texture) const;
void line2d(const Vector2& p0, const Vector2& p1, const Color4& color);
virtual void rect2dImpl(const Vector2& x0y0, const Vector2& x1y0, const Vector2& x0y1, const Vector2& x1y1, const Vector2& tex0,
const Vector2& tex1, const Color4& color);
Vector2 get2DStringBounds(const std::string& s, float size, Text::Font font, const Vector2& availableSpace) const;
Vector2 drawFont2DImpl(Adorn* target, const std::string& s, const Vector2& pos2D, float size, bool autoScale, const Color4& color,
const Color4& outline, Text::Font font, Text::XAlign xalign, Text::YAlign yalign, const Vector2& availableSpace, const Rect2D& clippingRect,
const Rotation2D& rotation);
const Camera* getCamera() const
{
return 0;
}
TextureProxyBaseRef createTextureProxy(const ContentId& id, bool& waiting, bool bBlocking, const std::string& context = "")
{
return parent->createTextureProxy(id, waiting, bBlocking, context);
}
TextureProxyBaseRef createTextureProxy(int width, int height)
{
return parent->createTextureProxy(width, height);
};
Aya::signal<void()>& getUnbindResourcesSignal()
{
return parent->getUnbindResourcesSignal();
}
Rect2D getViewport() const;
void setObjectToWorldMatrix(const CoordinateFrame& c)
{
;
}
void line3d(const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color)
{
;
}
void line3dAA(const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color, float thickness, int zIndex, bool alwaysOnTop)
{
;
}
void box(const AABox& b, const Color4& solidColor)
{
;
}
void box(const CoordinateFrame& cFrame, const Vector3& size, const Color4& color, int zIndex, bool alwaysOnTop)
{
;
}
void sphere(const Sphere& s, const Color4& solidColor)
{
;
}
void sphere(const CoordinateFrame& cFrame, float radius, const Color4& color, int zIndex, bool alwaysOnTop)
{
;
}
void explosion(const Sphere& sphere)
{
;
}
void cylinder(const CoordinateFrame& cFrame, float radius, float height, const Color4& color, int zIndex, bool alwaysOnTop)
{
;
}
void cylinderAlongX(float radius, float length, const Color4& solidColor, bool cap)
{
;
}
void cone(const CoordinateFrame& cFrame, float radius, float height, const Color4& color, int zIndex, bool alwaysOnTop)
{
;
}
void ray(const RbxRay& ray, const Color4& color)
{
;
}
void axes(const Color4&, const Color4&, const Color4&, float)
{
;
}
void quad(
const Vector3&, const Vector3&, const Vector3&, const Vector3&, const Color4&, const Vector2&, const Vector2&, int zIndex, bool alwaysOnTop)
{
;
}
void convexPolygon(const Vector3*, int, const Color4&)
{
;
}
void convexPolygon2d(const Vector2*, int, const Color4&)
{
;
}
void extrusion(Aya::I3DLinearFunc*, int, Aya::I3DLinearFunc*, int, const Color4&, bool, bool)
{
;
}
};
} // namespace Aya

View File

@@ -0,0 +1,45 @@
#pragma once
#include "DataModel/ContentProvider.hpp"
namespace Aya
{
class AsyncResult
{
public:
AsyncResult()
: reqResult(Aya::AsyncHttpQueue::Succeeded){};
// make result always more restrictive only.
// Succeeded < Waiting < Failed.
void returnResult(Aya::AsyncHttpQueue::RequestResult reqResult)
{
switch (reqResult)
{
case Aya::AsyncHttpQueue::Succeeded:
break;
case Aya::AsyncHttpQueue::Waiting:
if (this->reqResult == Aya::AsyncHttpQueue::Succeeded)
{
this->reqResult = reqResult;
}
break;
case Aya::AsyncHttpQueue::Failed:
this->reqResult = reqResult;
break;
}
}
void returnWaitingFor(const Aya::ContentId& id)
{
returnResult(Aya::AsyncHttpQueue::Waiting);
waitingFor.push_back(id);
}
Aya::AsyncHttpQueue::RequestResult reqResult;
std::vector<Aya::ContentId> waitingFor;
};
} // namespace Aya

View File

@@ -0,0 +1,420 @@
#include "Base/FileMeshData.hpp"
#include "Base/ObjLoader.hpp"
#include "Debug.hpp"
#include "DenseHash.hpp"
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
namespace Aya
{
struct MeshVertexHasher
{
bool operator()(const FileMeshVertexNormalTexture3d& l, const FileMeshVertexNormalTexture3d& r) const
{
return memcmp(&l, &r, sizeof(l)) == 0;
}
size_t operator()(const FileMeshVertexNormalTexture3d& v) const
{
size_t result = 0;
boost::hash_combine(result, v.vx);
boost::hash_combine(result, v.vy);
boost::hash_combine(result, v.vz);
return result;
}
};
void optimizeMesh(FileMeshData& mesh)
{
std::vector<unsigned int> remap(mesh.vnts.size());
FileMeshVertexNormalTexture3d dummy = {};
dummy.vx = FLT_MAX;
typedef DenseHashMap<FileMeshVertexNormalTexture3d, unsigned int, MeshVertexHasher, MeshVertexHasher> VertexMap;
VertexMap vertexMap(dummy);
for (size_t i = 0; i < mesh.vnts.size(); ++i)
{
unsigned int& vi = vertexMap[mesh.vnts[i]];
if (vi == 0)
vi = vertexMap.size();
remap[i] = vi - 1;
}
std::vector<FileMeshVertexNormalTexture3d> newvnts(vertexMap.size());
for (size_t i = 0; i < mesh.vnts.size(); ++i)
newvnts[remap[i]] = mesh.vnts[i];
mesh.vnts.swap(newvnts);
for (size_t i = 0; i < mesh.faces.size(); ++i)
{
FileMeshFace& face = mesh.faces[i];
face.a = remap[face.a];
face.b = remap[face.b];
face.c = remap[face.c];
}
}
inline unsigned int atouFast(const char* value, const char** end)
{
const char* s = value;
// skip whitespace
while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n')
s++;
// read integer part
unsigned int result = 0;
while (static_cast<unsigned int>(*s - '0') < 10)
{
result = result * 10 + (*s - '0');
s++;
}
// done!
*end = s;
return result;
}
inline double atofFast(const char* value, const char** end)
{
static const double digits[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
static const double powers[] = {1e0, 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17,
1e+18, 1e+19, 1e+20, 1e+21, 1e+22};
const char* s = value;
// skip whitespace
while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n')
s++;
// read sign
double sign = (*s == '-') ? -1 : 1;
s += (*s == '-' || *s == '+');
// read integer part
double result = 0;
int power = 0;
while (static_cast<unsigned int>(*s - '0') < 10)
{
result = result * 10 + digits[*s - '0'];
s++;
}
// read fractional part
if (*s == '.')
{
s++;
while (static_cast<unsigned int>(*s - '0') < 10)
{
result = result * 10 + digits[*s - '0'];
s++;
power--;
}
}
// read exponent part
if ((*s | ' ') == 'e')
{
s++;
// read exponent sign
int expsign = (*s == '-') ? -1 : 1;
s += (*s == '-' || *s == '+');
// read exponent
int exppower = 0;
while (static_cast<unsigned int>(*s - '0') < 10)
{
exppower = exppower * 10 + (*s - '0');
s++;
}
// done!
power += expsign * exppower;
}
// done!
*end = s;
if (static_cast<unsigned int>(-power) < sizeof(powers) / sizeof(powers[0]))
return sign * result / powers[-power];
else if (static_cast<unsigned int>(power) < sizeof(powers) / sizeof(powers[0]))
return sign * result * powers[power];
else
return sign * result * powf(10.0, power);
}
inline const char* readToken(const char* data, char terminator)
{
while (*data == ' ' || *data == '\t' || *data == '\r' || *data == '\n')
++data;
if (*data != terminator)
throw Aya::runtime_error("Error reading mesh data: expected %c", terminator);
return data + 1;
}
inline const char* readFloatToken(const char* data, char terminator, float* output)
{
const char* end;
double value = atofFast(data, &end);
if (*end != terminator)
throw Aya::runtime_error("Error reading mesh data: expected %c", terminator);
*output = value;
return end + 1;
}
shared_ptr<FileMeshData> readMeshFromV1(const std::string& data, size_t offset_, float scaler)
{
shared_ptr<FileMeshData> mesh(new FileMeshData());
const char* offset = data.c_str() + offset_;
unsigned int num_faces = atouFast(offset, &offset);
mesh->vnts.reserve(num_faces * 3);
mesh->faces.reserve(num_faces);
for (unsigned int i = 0; i < num_faces; i++)
{
for (int v = 0; v < 3; v++)
{
float vx, vy, vz, nx, ny, nz, tu, tv, tw;
offset = readToken(offset, '[');
offset = readFloatToken(offset, ',', &vx);
offset = readFloatToken(offset, ',', &vy);
offset = readFloatToken(offset, ']', &vz);
offset = readToken(offset, '[');
offset = readFloatToken(offset, ',', &nx);
offset = readFloatToken(offset, ',', &ny);
offset = readFloatToken(offset, ']', &nz);
offset = readToken(offset, '[');
offset = readFloatToken(offset, ',', &tu);
offset = readFloatToken(offset, ',', &tv);
offset = readFloatToken(offset, ']', &tw);
G3D::Vector3 normal = G3D::Vector3(nx, ny, nz).unit();
if (!normal.isFinite())
normal = G3D::Vector3::zero();
FileMeshVertexNormalTexture3d vtx = {vx * scaler, vy * scaler, vz * scaler, normal.x, normal.y, normal.z, tu, 1.f - tv, tw};
mesh->vnts.push_back(vtx);
}
FileMeshFace face = {i * 3 + 0, i * 3 + 1, i * 3 + 2};
mesh->faces.push_back(face);
}
return mesh;
}
static void readData(const std::string& data, size_t& offset, void* buffer, size_t size)
{
if (offset + size > data.size())
throw std::runtime_error("Error reading mesh data: offset is out of bounds while reading");
memcpy(buffer, data.data() + offset, size);
offset += size;
}
static shared_ptr<FileMeshData> readMeshFromV2(const std::string& data, size_t offset)
{
shared_ptr<FileMeshData> mesh(new FileMeshData());
FileMeshHeader header;
readData(data, offset, &header, sizeof(header));
if (header.cbSize != sizeof(FileMeshHeader) ||
(header.cbVerticesStride != sizeof(FileMeshVertexNormalTexture3d) &&
header.cbVerticesStride != sizeof(FileMeshVertexNormalTexture3d) + 4) || // Check for both possible vertex sizes
header.cbFaceStride != sizeof(FileMeshFace))
{
throw std::runtime_error("Error reading mesh data: incompatible stride");
}
if (header.num_vertices == 0 || header.num_faces == 0)
throw std::runtime_error("Error reading mesh data: empty mesh");
mesh->vnts.resize(header.num_vertices);
size_t vertexSize = sizeof(FileMeshVertexNormalTexture3d);
size_t skipSize = (header.cbVerticesStride == vertexSize + 4) ? 4 : 0; // Determine whether to skip RGBA
for (size_t i = 0; i < header.num_vertices; ++i)
{
readData(data, offset, &mesh->vnts[i], vertexSize);
offset += skipSize; // Skip RGBA data if necessary
}
mesh->faces.resize(header.num_faces);
readData(data, offset, &mesh->faces[0], header.num_faces * header.cbFaceStride);
if (offset != data.size())
throw std::runtime_error("Error reading mesh data: unexpected data at end of file");
// Validate indices to avoid buffer overruns later
for (auto& face : mesh->faces)
if (face.a >= header.num_vertices || face.b >= header.num_vertices || face.c >= header.num_vertices)
throw std::runtime_error("Error reading mesh data: index value out of range");
return mesh;
}
FileMeshData* computeAABB(FileMeshData* mesh)
{
if (mesh->vnts.empty())
{
mesh->aabb = AABox(Vector3::zero());
}
else
{
AABox result = AABox(Vector3(mesh->vnts[0].vx, mesh->vnts[0].vy, mesh->vnts[0].vz));
for (size_t i = 1; i < mesh->vnts.size(); ++i)
result.merge(Vector3(mesh->vnts[i].vx, mesh->vnts[i].vy, mesh->vnts[i].vz));
mesh->aabb = result;
}
return mesh;
}
static shared_ptr<FileMeshData> readGenericMesh(const std::string& data)
{
shared_ptr<FileMeshData> mesh(new FileMeshData());
Assimp::Importer importer;
const aiScene* scene =
importer.ReadFileFromMemory(data.data(), data.size(), aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_CalcTangentSpace);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
{
throw std::runtime_error("Failed to load mesh: " + std::string(importer.GetErrorString()));
}
// Assuming we're only interested in the first mesh
if (scene->mNumMeshes > 0)
{
const aiMesh* aiMesh = scene->mMeshes[0];
// Read vertices, normals, and texture coordinates
for (unsigned int i = 0; i < aiMesh->mNumVertices; ++i)
{
FileMeshVertexNormalTexture3d vnt;
vnt.vx = aiMesh->mVertices[i].x;
vnt.vy = aiMesh->mVertices[i].y;
vnt.vz = aiMesh->mVertices[i].z;
if (aiMesh->HasNormals())
{
vnt.nx = aiMesh->mNormals[i].x;
vnt.ny = aiMesh->mNormals[i].y;
vnt.nz = aiMesh->mNormals[i].z;
}
if (aiMesh->HasTextureCoords(0))
{
vnt.tu = aiMesh->mTextureCoords[0][i].x;
vnt.tv = aiMesh->mTextureCoords[0][i].y;
vnt.tw = aiMesh->mTextureCoords[0][i].z;
}
mesh->vnts.push_back(vnt);
}
// Read faces
for (unsigned int i = 0; i < aiMesh->mNumFaces; ++i)
{
const aiFace& face = aiMesh->mFaces[i];
if (face.mNumIndices == 3)
{
FileMeshFace meshFace;
meshFace.a = face.mIndices[0];
meshFace.b = face.mIndices[1];
meshFace.c = face.mIndices[2];
mesh->faces.push_back(meshFace);
}
}
}
else
{
throw std::runtime_error("No meshes found in the imported file");
}
return mesh;
}
} // namespace Aya
namespace Aya
{
shared_ptr<FileMeshData> ReadFileMesh(const std::string& data)
{
shared_ptr<FileMeshData> result;
std::string::size_type versionEnd = data.find('\n');
if (versionEnd == std::string::npos)
throw std::runtime_error("Error reading mesh data: unknown version");
if (data.compare(0, 12, "version 1.00") == 0)
result = readMeshFromV1(data, versionEnd + 1, 0.5f);
else if (data.compare(0, 12, "version 1.01") == 0)
result = readMeshFromV1(data, versionEnd + 1, 1.0f);
else if (data.compare(0, 12, "version 2.00") == 0)
result = readMeshFromV2(data, versionEnd + 1);
else
{
try
{
result = readGenericMesh(data);
}
catch (...)
{
throw std::runtime_error("Error reading mesh data: unknown version");
}
}
optimizeMesh(*result);
computeAABB(result.get());
return result;
}
void WriteFileMesh(std::ostream& f, const FileMeshData& data)
{
f << "version 2.00" << std::endl;
FileMeshHeader header;
header.num_faces = data.faces.size();
header.num_vertices = data.vnts.size();
header.cbFaceStride = (unsigned char)sizeof(data.faces[0]);
header.cbVerticesStride = (unsigned char)sizeof(data.vnts[0]);
header.cbSize = sizeof(header);
f.write(reinterpret_cast<char*>(&header), sizeof(header));
f.write(reinterpret_cast<const char*>(&data.vnts[0]), sizeof(data.vnts[0]) * data.vnts.size());
f.write(reinterpret_cast<const char*>(&data.faces[0]), sizeof(data.faces[0]) * data.faces.size());
}
} // namespace Aya

View File

@@ -0,0 +1,24 @@
#pragma once
#include "MeshFileStructs.hpp"
#include "Utility/Object.hpp"
#include "Utility/G3DCore.hpp"
#include <vector>
namespace Aya
{
struct FileMeshData
{
std::vector<FileMeshVertexNormalTexture3d> vnts;
std::vector<FileMeshFace> faces;
AABox aabb;
};
shared_ptr<FileMeshData> ReadFileMesh(const std::string& data);
// writes the newest version always.
// remember: set ostream to binary!
void WriteFileMesh(std::ostream& f, const FileMeshData& mesh);
} // namespace Aya

View File

@@ -0,0 +1,931 @@
#include "Base/FrameRateManager.hpp"
#include "Base/RenderCaps.hpp"
#include "Debug.hpp"
#include "Log.hpp"
#include "FastLog.hpp"
#include "AyaFormat.hpp"
#include "TaskScheduler.hpp"
#include "Utility/Math.hpp"
#include "SystemUtil.hpp"
#include "DataModel/GameBasicSettings.hpp"
#include <functional>
LOGGROUP(FRM)
#ifdef AYA_TEST_BUILD
FASTFLAGVARIABLE(DebugSSAOForce, true)
#else
FASTFLAGVARIABLE(DebugSSAOForce, false)
#endif
FASTINTVARIABLE(FRMRecomputeDistanceFrameDelay, 100)
FASTINTVARIABLE(RenderGBufferMinQLvl, 20) // 14 for later
FASTFLAGVARIABLE(DebugSSAOForceDisable, false) // overrides DebugSSAOForce
namespace Aya
{
/////////////////////////////////////////////////////////////////////////////
// Tweakable section
/////////////////////////////////////////////////////////////////////////////
static const int AveragingFrames = 40;
static const int VarianceFrames = 20;
static const int LockStepDelayDown = 100; // Number of frames to wait after going a quality level down
static const int LockStepDelayUp = 150; // Number of frames to wait after going a quality level up
static const double RenderFraction = 0.625;
static const double VarianceLimit = 5;
static const double MultiCoreRenderBottleneckFraction = 0.8;
static const double MultiCorePrepareFraction = 0.3;
static const int SwitchCounterMax = 10;
static const int SettleDelay = 20; // Number of milliseconds that we consider level stable
// Fast backoff filter:
// If during LockStepDelayDown your average frame length (averaged by FastBackoffFPSAve) is more than MaxFrameLen...
// ... consecutively for WatchingFrames frames
// you're going to be backed off to previous level
// ... with StepLevel increased by FastBackoffStepLevelIncrement
#if defined(AYA_PLATFORM_IOS) || defined(__ANDROID__)
static const double FastBackoffMaxFrameLen = 40; // 25 FPS
#else
static const double FastBackoffMaxFrameLen = 60; // 16.6 FPS
#endif
static const int FastBackoffFPSAve = 10; // frames
static const int FastBackoffWatchingFrames = 5; // frames
static const int SqDistanceBump = 50;
struct THROTTLE_LOCKSTEP
{
double framerate;
float distance;
int blockCount;
float shadingDistance;
int textureAnisotropy;
SSAOLevel ssao;
float lightGridRadius;
bool lightAllowNonFixed;
unsigned lightChunkBudget;
int throttlingFactor; // Matches physics throttling table in World.cpp: - 0/8, 1/8, 1/4, 1/3, 1/2, 2/3, 3/4, 7/8, 15/16
double StepHill;
double MaxStepHill;
};
inline float sqrf(float value)
{
return value * value;
}
static THROTTLE_LOCKSTEP kLockstepTable60FPS[] = {
// Quality levels:
{std::numeric_limits<double>::max(), 100000.0f, 1000000, 300, 8, ssaoFull, 512, true, 4, 0, 10, 10},
{std::numeric_limits<double>::max(), 100000.0f, 100000, 300, 8, ssaoFull, 512, false, 4, 8, 5, 10}, // Level 1
{80 /* 12 FPS */, 100000.0f, 100000, 300, 8, ssaoFull, 512, false, 4, 7, 5, 10}, // Level 2
{66 /* 15 FPS */, 100000.0f, 100000, 300, 8, ssaoFull, 512, false, 4, 6, 5, 10}, // Level 3
{50 /* 20 FPS */, 100000.0f, 100000, 300, 8, ssaoFull, 512, false, 4, 5, 5, 10}, // Level 4
{
42 /* 25 FPS */,
100000.0f,
100000,
300,
8,
ssaoFull,
512,
false,
4,
4,
5,
10,
}, // Level 5
{
40 /* 25 FPS */,
100000.0f,
100000,
300,
8,
ssaoFull,
512,
false,
4,
3,
5,
10,
}, // Level 6
{
35 /* 28 FPS */,
100000.0f,
100000,
300,
8,
ssaoFull,
512,
false,
4,
2,
5,
10,
}, // Level 7
{
35 /* 28 FPS */,
100000.0f,
100000,
300,
8,
ssaoFull,
512,
false,
4,
1,
5,
10,
}, // Level 8
{
35 /* 28 FPS */,
100000.0f,
100000,
300,
8,
ssaoFull,
512,
false,
4,
0,
5,
10,
}, // Level 9
{
35 /* 28 FPS */,
100000.0f,
100000,
300,
8,
ssaoFull,
512,
true,
4,
0,
5,
10,
}, // Level 10
{
35 /* 28 FPS */,
100000.0f,
100000,
300,
8,
ssaoFull,
512,
true,
4,
0,
5,
10,
}, // Level 11
{
33 /* 30 FPS */,
100000.0f,
100000,
300,
8,
ssaoFull,
512,
true,
4,
0,
4,
8,
}, // Level 12
{
30 /* 33 FPS */,
100000.0f,
100000,
300,
8,
ssaoFull,
512,
true,
4,
0,
4,
8,
}, // Level 13
{
27 /* 37 FPS */,
100000.0f,
100000,
300,
8,
ssaoFull,
512,
true,
4,
0,
4,
8,
}, // Level 14
{
25 /* 40 FPS */,
100000.0f,
100000,
300,
8,
ssaoFull,
512,
true,
4,
0,
4,
8,
}, // Level 15
{
23 /* 43 FPS */,
100000.0f,
100000,
300,
8,
ssaoFull,
512,
true,
4,
0,
4,
8,
}, // Level 16
{
20 /* 50 FPS */,
100000.0f,
100000,
300,
8,
ssaoFull,
512,
true,
4,
0,
2,
2,
}, // Level 17
{
19 /* 60 FPS */,
100000.0f,
100000,
300,
8,
ssaoFull,
512,
true,
4,
0,
2,
2,
}, // Level 18
{
19 /* 60 FPS */,
100000.0f,
100000,
300,
8,
ssaoFull,
512,
true,
4,
0,
2,
2,
}, // Level 19
{
19 /* 60 FPS */,
100000.0f,
100000,
300,
8,
ssaoFull,
512,
true,
4,
0,
2,
2,
}, // Level 20
{
19 /* 60 FPS */,
100000.0f,
100000,
300,
8,
ssaoFull,
512,
true,
4,
0,
2,
2,
} // Level 21
};
/////////////////////////////////////////////////////////////////////////////
// Less tweakable, but still
/////////////////////////////////////////////////////////////////////////////
FrameRateManager::FrameRateManager(void)
: mSettings(0)
, mRenderCaps(0)
, mBlockCullingEnabled(true)
, mStableFramesCounter(0)
, mThrottlingOn(false)
, mCurrentQualityLevel(0)
, frameTimeAverage(AveragingFrames)
, renderTimeAverage(AveragingFrames)
, prepareTimeAverage(AveragingFrames)
, frameTimeVarianceAverage(VarianceFrames)
, fastBackoffAverage(FastBackoffFPSAve)
, mQualityDelayDown(LockStepDelayDown)
, mQualityDelayUp(LockStepDelayDown)
, mWasQualityUp(false)
, mSwitchCounter(1)
, mIsStable(false)
, mBlockCounter(0)
, mLastBlockCounter(0)
, mAdjustmentOn(true)
, mBadBackoffFrameCounter(0)
, mRecomputeDistanceDelay(FInt::FRMRecomputeDistanceFrameDelay)
, mAggressivePerformance(false)
{
AYAASSERT(CRenderSettings::QualityLevelMax ==
ARRAYSIZE(kLockstepTable60FPS)); // If that fails, you probably added another quality level without syncing it with RenderSettings
LockstepTable = kLockstepTable60FPS;
AYAASSERT(LockStepDelayDown <= LockStepDelayUp);
// We need to have enough frames for averaging before we can step down again
AYAASSERT(AveragingFrames + VarianceFrames <= LockStepDelayDown);
for (unsigned i = 0; i < CRenderSettings::QualityLevelMax; ++i)
mQualityCount[i] = 0;
// Sensible defaults for culling
mSqDistance = LockstepTable[0].distance * LockstepTable[0].distance;
mSqRenderDistance = mSqDistance;
}
void FrameRateManager::configureFrameRateManager(CRenderSettings::FrameRateManagerMode mode, bool hasCharacter)
{
if (hasCharacter)
{
SetBlockCullingEnabled(mode == CRenderSettings::FrameRateManagerOff ? false : true);
}
else
{
SetBlockCullingEnabled(mode == CRenderSettings::FrameRateManagerOn ? true : false);
}
}
void FrameRateManager::setAggressivePerformance(bool value)
{
mAggressivePerformance = value;
}
CRenderSettings::AntialiasingMode FrameRateManager::getAntialiasingMode()
{
switch (mSettings->getAntialiasingMode())
{
case CRenderSettings::AntialiasingAuto:
// return mRenderCaps->getBestAntialiasingMode();
return CRenderSettings::AntialiasingOff;
// other settings simply override.
default:
return mSettings->getAntialiasingMode();
}
}
void FrameRateManager::updateMaxSettings()
{
mSSAOSupported = mRenderCaps->getSupportsGBuffer();
}
/////////////////////////////////////////////////////////////////////////////
// End of tweakable section
/////////////////////////////////////////////////////////////////////////////
FrameRateManager::~FrameRateManager(void)
{
SendQualityLevelStats();
}
void FrameRateManager::SendQualityLevelStats() {}
float FrameRateManager::GetAvarageQuality()
{
// compute average quality and send it to GA. Trying to keep the precision
float floatCounts[CRenderSettings::QualityLevelMax];
float freqSum = 0;
for (unsigned i = 1; i < CRenderSettings::QualityLevelMax; ++i) // we ignore quality lvl = 0
{
floatCounts[i] = mQualityCount[i];
freqSum += mQualityCount[i];
}
// if there is less then 100 samples, there is really nothing to report
if (freqSum > 100)
{
float avgQuality = 0;
float freqSumInv = 1.0f / freqSum;
for (unsigned i = 0; i < CRenderSettings::QualityLevelMax; ++i)
avgQuality += i * floatCounts[i] * freqSumInv;
return avgQuality;
}
else
return 0;
}
float FrameRateManager::GetTargetFrameTime(int level) const
{
return mAggressivePerformance ? 19.f : LockstepTable[level].framerate;
}
void FrameRateManager::AddBlockQuota(int blocksInCluster, float sqDistanceToCamera, bool isInSpatialHash)
{
if (!mIsGatheringDistance)
return;
mBlockCounter += blocksInCluster;
if (mBlockCounter >= mBlockTarget)
{
// if cluster is in spatial hash, call order is done in roughly increasing camera distance so we can assume that the value is a valid cut-off
// if cluster is not in spatial hash, calls are not ordered; we process all such clusters first, so if we ran out of blocks already, we have
// to resort to the minimal culling distance for the current level
if (isInSpatialHash)
mSqDistance = std::max(
LockstepTable[mCurrentQualityLevel].distance * LockstepTable[mCurrentQualityLevel].distance, sqDistanceToCamera + SqDistanceBump);
else
mSqDistance = LockstepTable[mCurrentQualityLevel].distance * LockstepTable[mCurrentQualityLevel].distance;
mIsGatheringDistance = false;
}
}
void FrameRateManager::SubmitCurrentFrame(double frameTime, double renderTime, double prepareTime, double bonusTime)
{
updateMaxSettings(); // do this in a safe place. doesn't like being changed mid-frame?
UpdateStats(frameTime, renderTime, prepareTime);
// Use the distance from last frame for render distance this frame
// If we were not gathering distance last frame they're the same
// If we *were* then this is the cutoff distance where we reached the necessary block count
mSqRenderDistance = mSqDistance;
if (mSettings->getEnableFRM())
{
if (mRecomputeDistanceDelay > 0)
mRecomputeDistanceDelay--;
else
{
FASTLOG1(FLog::FRM, "Recomputing gathering distance on level %u", mCurrentQualityLevel);
// Temporarily unlock the culling distance for one frame
mSqDistance = LockstepTable[0].distance * LockstepTable[0].distance;
mRecomputeDistanceDelay = FInt::FRMRecomputeDistanceFrameDelay;
mIsGatheringDistance = true;
}
if (!mThrottlingOn)
{
// Initialize quality level for playing
mThrottlingOn = true;
CRenderSettings::QualityLevel qualityLevel = mSettings->getQualityLevel();
if (qualityLevel == CRenderSettings::QualityAuto)
{
int autoQualityLevel = mSettings->getAutoQualityLevel();
mCurrentQualityLevel = std::max(1, std::min(autoQualityLevel, (int)CRenderSettings::QualityLevelMax - 1));
}
else
{
mCurrentQualityLevel = qualityLevel;
}
FASTLOG2(FLog::FRM, "Starting FRM, Quality setting: %u, starting level: %u", qualityLevel, mCurrentQualityLevel);
UpdateQualitySettings();
}
else
{
CRenderSettings::QualityLevel qualityLevel = mSettings->getQualityLevel();
bool bAdjusmentOn = false;
if (qualityLevel == CRenderSettings::QualityAuto)
{
bAdjusmentOn = mAdjustmentOn;
}
else if (qualityLevel != mCurrentQualityLevel)
{
mCurrentQualityLevel = qualityLevel;
UpdateQualitySettings();
}
AdjustQuality(frameTime, renderTime, bAdjusmentOn, bonusTime);
}
}
else
{
mThrottlingOn = false;
mCurrentQualityLevel = mSettings->getEditQualityLevel();
UpdateQualitySettings();
}
mLastBlockCounter = mBlockCounter;
if (mIsGatheringDistance)
mBlockCounter = 0;
}
void FrameRateManager::StartCapturingMetrics()
{
memset(&mMetrics, 0, sizeof(mMetrics));
mIsStable = false;
mSettleTimer.reset();
}
void FrameRateManager::UpdateStats(double frameTime, double renderTime, double prepareTime)
{
if (fabs(frameTime) < 0.001 || fabs(renderTime) < 0.001)
{
return;
}
frameTimeAverage.sample(frameTime);
renderTimeAverage.sample(renderTime);
prepareTimeAverage.sample(prepareTime);
fastBackoffAverage.sample(frameTime);
mFPSCounter.Update(frameTime);
if (mCurrentQualityLevel > 0)
{
// prevent overflow (really unlike, but still)
if (mQualityCount[mCurrentQualityLevel] == UINT_MAX - 1)
for (unsigned i = 0; i < CRenderSettings::QualityLevelMax; ++i)
mQualityCount[i] /= 2;
++mQualityCount[mCurrentQualityLevel];
}
}
float FrameRateManager::GetTargetFrameTimeForNextLevel() const
{
AYAASSERT(mCurrentQualityLevel < (CRenderSettings::QualityLevelMax - 1));
return GetTargetFrameTime(mCurrentQualityLevel + 1);
}
float FrameRateManager::GetTargetRenderTimeForNextLevel() const
{
AYAASSERT(mCurrentQualityLevel < (CRenderSettings::QualityLevelMax - 1));
return GetTargetFrameTime(mCurrentQualityLevel + 1) * MultiCoreRenderBottleneckFraction - LockstepTable[mCurrentQualityLevel + 1].StepHill;
}
void FrameRateManager::AdjustQuality(double frameTime, double renderTime, bool adjustmentOn, double bonusTime)
{
if (fabs(frameTime) < 0.001 || fabs(renderTime) < 0.001 || !adjustmentOn)
return;
// Don't adjust until we have delayed enough
if (mQualityDelayDown > 0)
mQualityDelayDown--;
if (mQualityDelayUp > 0)
mQualityDelayUp--;
Aya::WindowAverage<double, double>::Stats frameStats = frameTimeAverage.getStats();
Aya::WindowAverage<double, double>::Stats renderStats = renderTimeAverage.getStats();
Aya::WindowAverage<double, double>::Stats prepareStats = prepareTimeAverage.getStats();
frameTimeVarianceAverage.sample(frameStats.average);
frameStats.average -= bonusTime;
renderStats.average -= bonusTime;
prepareStats.average -= bonusTime;
FASTLOG3F(FLog::FRM, "FRM status. Frame time average: %f, Delay up %f, Delay down %f", frameStats.average, (float)mQualityDelayUp,
(float)mQualityDelayDown);
Aya::WindowAverage<double, double>::Stats fastBackoffStats = fastBackoffAverage.getStats();
fastBackoffStats.average -= bonusTime;
if (fastBackoffStats.average > FastBackoffMaxFrameLen && FastBackoffMaxFrameLen > GetTargetFrameTime(mCurrentQualityLevel - 1))
mBadBackoffFrameCounter++;
else
mBadBackoffFrameCounter = 0;
if (mBadBackoffFrameCounter >= FastBackoffWatchingFrames && mCurrentQualityLevel > 1)
{
FASTLOG(FLog::FRM, "FastBackoff, reducing quality");
StepQuality(false, true);
}
if (mQualityDelayDown > 0 && mQualityDelayUp > 0)
return;
Aya::WindowAverage<double, double>::Stats frameAverageStats = frameTimeVarianceAverage.getStats();
if (frameAverageStats.variance > VarianceLimit)
return;
bool bRenderLimited = renderStats.average > frameStats.average * RenderFraction;
if (Aya::TaskScheduler::singleton().getThreadCount() > 1)
bRenderLimited = renderStats.average > frameStats.average * MultiCoreRenderBottleneckFraction;
// Check for going down:
if ((mQualityDelayDown == 0) && (mCurrentQualityLevel > 1) && (frameStats.average > GetTargetFrameTime(mCurrentQualityLevel)) && bRenderLimited)
StepQuality(false, false);
// Check for going up:
else if ((mQualityDelayUp == 0) && (mCurrentQualityLevel < (CRenderSettings::QualityLevelMax - 1)) &&
frameStats.average < GetTargetFrameTimeForNextLevel())
{
bool renderingHasRoom = renderStats.average < GetTargetRenderTimeForNextLevel();
if (Aya::TaskScheduler::singleton().getThreadCount() > 1)
{
renderingHasRoom = (renderStats.average < GetTargetRenderTimeForNextLevel()) &&
prepareStats.average < GetTargetFrameTime(mCurrentQualityLevel + 1) * MultiCorePrepareFraction;
}
if (renderingHasRoom)
StepQuality(true, false);
}
if (!mIsStable && mSettleTimer.delta().seconds() > SettleDelay)
{
mIsStable = true;
mMetrics.NumberOfSettles++;
}
}
void FrameRateManager::StepQuality(bool stepUp, bool isBackOff)
{
int oldQualityLevel = mCurrentQualityLevel;
mCurrentQualityLevel += stepUp ? 1 : -1;
FASTLOG2(FLog::FRM, "Stepping FRM quality, old: %u, new : %u", oldQualityLevel, mCurrentQualityLevel);
UpdateQualitySettings();
frameTimeAverage.clear();
renderTimeAverage.clear();
// Make delay for stepping down constant
mQualityDelayDown = LockStepDelayDown;
mBadBackoffFrameCounter = 0;
mQualityDelayUp = stepUp ? LockStepDelayUp : LockStepDelayUp * mSwitchCounter;
if (mCurrentQualityLevel > 1)
{
// If last step was down, make going up harder
if (stepUp != mWasQualityUp && mSwitchCounter < SwitchCounterMax)
{
// If we're stepping down from higher level immediately, bump the step (within the allowed range, of course)
if (!stepUp)
{
AYAASSERT(mCurrentQualityLevel < (CRenderSettings::QualityLevelMax - 1));
int previousLevel = mCurrentQualityLevel + 1;
double StepHillAdd = isBackOff ? 0.1 : 1;
LockstepTable[previousLevel].StepHill =
std::min(LockstepTable[previousLevel].MaxStepHill, LockstepTable[previousLevel].StepHill + StepHillAdd);
}
mSwitchCounter++;
}
else if (mSwitchCounter > 1)
{
mSwitchCounter--;
}
}
mWasQualityUp = stepUp;
mSettings->setAutoQualityLevel(mCurrentQualityLevel);
mSettleTimer.reset();
mIsStable = false;
// Accumulate number of switches here, average it on GetMetrics
mMetrics.AverageSwitchesPerSettle++;
}
FrameRateManager::Metrics FrameRateManager::GetMetrics()
{
Metrics result = mMetrics;
CRenderSettings::QualityLevel qualityLevel = mSettings->getQualityLevel();
result.AutoQuality = qualityLevel == CRenderSettings::QualityAuto;
result.QualityLevel = Math::iRound(GetAvarageQuality());
result.AverageFps = mFPSCounter.GetFPS();
if (result.NumberOfSettles != 0)
result.AverageSwitchesPerSettle /= result.NumberOfSettles;
return result;
}
void FrameRateManager::UpdateQualitySettings()
{
const THROTTLE_LOCKSTEP& lockstep = LockstepTable[mCurrentQualityLevel];
if (mSettings->getFrameRateManagerMode() != CRenderSettings::FrameRateManagerOff)
mBlockTarget = lockstep.blockCount;
else
mBlockTarget = LockstepTable[0].blockCount;
mIsGatheringDistance = true;
// Unlock the view distance but don't change rendering distance; we'll recompute it this frame
mSqDistance = LockstepTable[0].distance * LockstepTable[0].distance;
mRecomputeDistanceDelay = FInt::FRMRecomputeDistanceFrameDelay;
}
double FrameRateManager::getMetricValue(const std::string& metric)
{
if (metric == "FRM")
return IsBlockCullingEnabled();
else if (metric == "FRM Target")
return GetVisibleBlockTarget();
else if (metric == "FRM Visible")
return GetVisibleBlockCounter();
else if (metric == "FRM Distance")
return sqrt(GetViewCullSqDistance());
else if (metric == "FRM Quality")
return GetQualityLevel();
else if (metric == "FRM Auto Quality")
return mSettings->getQualityLevel() == CRenderSettings::QualityAuto;
else if (metric == "FRM Switch Counter")
return mSwitchCounter;
else if (metric == "FRM Step Hill")
{
// If Quality is not allowed to be adjusted, return -1
if (mSettings->getQualityLevel() != CRenderSettings::QualityAuto)
return -1;
else
return mCurrentQualityLevel + 1 < CRenderSettings::QualityLevelMax ? LockstepTable[mCurrentQualityLevel + 1].StepHill : 0;
}
else if (metric == "FRM Adjust Delay Up")
return mQualityDelayUp;
else if (metric == "FRM Adjust Delay Down")
return mQualityDelayDown;
else if (metric == "FRM Variance")
return frameTimeVarianceAverage.getStats().variance;
else if (metric == "FRM Backoff Counter")
return mBadBackoffFrameCounter;
else if (metric == "FRM Backoff Average")
return fastBackoffAverage.getStats().average;
return -1;
}
double FrameRateManager::GetFrameTimeAverage()
{
return frameTimeAverage.getStats().average;
}
double FrameRateManager::GetPrepareTimeAverage()
{
return prepareTimeAverage.getStats().average;
}
double FrameRateManager::GetRenderTimeAverage()
{
return renderTimeAverage.getStats().average;
}
const WindowAverage<double, double>& FrameRateManager::GetFrameTimeStats()
{
return frameTimeAverage;
}
const WindowAverage<double, double>& FrameRateManager::GetRenderTimeStats()
{
return renderTimeAverage;
}
float FrameRateManager::GetRenderCullSqDistance()
{
return mSqRenderDistance;
}
float FrameRateManager::GetViewCullSqDistance()
{
return mSqDistance;
}
float FrameRateManager::getShadingDistance() const
{
return LockstepTable[mCurrentQualityLevel].shadingDistance;
}
int FrameRateManager::getPhysicsThrottling() const
{
return LockstepTable[mCurrentQualityLevel].throttlingFactor;
}
float FrameRateManager::getShadingSqDistance() const
{
return sqrf(LockstepTable[mCurrentQualityLevel].shadingDistance);
}
int FrameRateManager::getTextureAnisotropy() const
{
return LockstepTable[mCurrentQualityLevel].textureAnisotropy;
}
float FrameRateManager::getLightGridRadius() const
{
return LockstepTable[mCurrentQualityLevel].lightGridRadius;
}
bool FrameRateManager::getLightingNonFixedEnabled() const
{
return LockstepTable[mCurrentQualityLevel].lightAllowNonFixed;
}
unsigned FrameRateManager::getLightingChunkBudget() const
{
return LockstepTable[mCurrentQualityLevel].lightChunkBudget;
}
double FrameRateManager::GetMaxNextViewCullDistance()
{
return sqrt(mSqDistance) * 1.1;
}
SSAOLevel FrameRateManager::getSSAOLevel()
{
if (FFlag::DebugSSAOForceDisable)
return ssaoNone;
if (FFlag::DebugSSAOForce)
return ssaoFull;
if (!mSSAOSupported)
return ssaoNone;
return LockstepTable[mCurrentQualityLevel].ssao;
}
void FrameRateManager::Configure(const RenderCaps* renderCaps, CRenderSettings* settings)
{
mSettings = settings;
mRenderCaps = renderCaps;
updateMaxSettings();
UpdateQualitySettings();
}
// returns overall particle throttle factor. Range ]0 .. 1] , 1 for full detail.
double FrameRateManager::GetParticleThrottleFactor()
{
if (GetQualityLevel() == 0)
return 1.0;
return std::max(0.0, std::min(1.0, (double)GetQualityLevel() / CRenderSettings::QualityLevelMax));
}
bool FrameRateManager::getGBufferSetting()
{
return true;
#if defined(AYA_PLATFORM_IOS) || defined(__ANDROID__)
return false;
#else
return (isSSAOSupported() && GetQualityLevel() >= FInt::RenderGBufferMinQLvl);
#endif
}
void FrameRateManager::PauseAutoAdjustment()
{
mAdjustmentOn = false;
}
void FrameRateManager::ResumeAutoAdjustment()
{
mAdjustmentOn = true;
}
} // namespace Aya

View File

@@ -0,0 +1,249 @@
#pragma once
#include <vector>
#pragma warning(push)
#pragma warning(disable : 4996) // disable -D_SCL_SECURE_NO_WARNING in ublas.
#include <boost/numeric/ublas/vector.hpp>
#pragma warning(pop)
#include "Base/RenderSettings.hpp"
#include "RunningAverage.hpp"
#include <map>
namespace Aya
{
class RenderCaps;
class Log;
enum SSAOLevel
{
ssaoNone = 0,
ssaoFullBlank,
ssaoFull
};
struct THROTTLE_LOCKSTEP;
class FrameRateManager
{
public:
FrameRateManager(void);
~FrameRateManager(void);
void configureFrameRateManager(CRenderSettings::FrameRateManagerMode mode, bool hasCharacter);
void setAggressivePerformance(bool value);
struct Metrics
{
bool AutoQuality;
int QualityLevel;
int NumberOfSettles;
double AverageSwitchesPerSettle;
double AverageFps;
};
// add to current frame counter.
void AddBlockQuota(int blocksInCluster, float sqDistanceToCamera, bool isInSpatialHash);
bool getGBufferSetting();
SSAOLevel getSSAOLevel();
bool isSSAOSupported()
{
return mSSAOSupported;
}
float getShadingDistance() const;
float getShadingSqDistance() const;
int getTextureAnisotropy() const;
int getPhysicsThrottling() const;
float getLightGridRadius() const;
bool getLightingNonFixedEnabled() const;
unsigned getLightingChunkBudget() const;
void SubmitCurrentFrame(double frameTime, double renderTime, double prepareTime, double bonusTime);
// adjusts quality to try to fit rendering to this timespan.
void ThrottleTo(double rendertime_ms);
double getMetricValue(const std::string& metric);
int GetRecomputeDistanceDelay()
{
return mRecomputeDistanceDelay;
}
float GetViewCullSqDistance();
float GetRenderCullSqDistance();
double GetMaxNextViewCullDistance(); // farthest cull distance possible in next frame.
int GetQualityLevel()
{
return mCurrentQualityLevel;
}
bool IsBlockCullingEnabled()
{
return mBlockCullingEnabled;
};
void SetBlockCullingEnabled(bool enabled)
{
mBlockCullingEnabled = enabled;
};
// supply the framerate manager with some special information that can be
// used to formulate exceptions.
void Configure(const RenderCaps* renderCaps, CRenderSettings* settings);
// after calling Configure, this gives our determination of the best
// possible quality we can acheive with certain features, and with current settings
CRenderSettings::AntialiasingMode getAntialiasingMode();
void updateMaxSettings();
double GetVisibleBlockTarget() const
{
return mBlockTarget;
}; // smoothed block target
double GetVisibleBlockCounter() const
{
return mLastBlockCounter;
};
float GetTargetFrameTimeForNextLevel() const;
float GetTargetRenderTimeForNextLevel() const;
// counter that indicates how many frames have elapsed with the block count in a stable state.
void ResetStableFramesCounter()
{
mStableFramesCounter = 0;
};
const int& GetStableFramesCounter()
{
return mStableFramesCounter;
};
// returns overall particle throttle factor. Range ]0 .. 1] , 1 for full detail.
double GetParticleThrottleFactor();
double GetRenderTimeAverage();
double GetPrepareTimeAverage();
double GetFrameTimeAverage();
const WindowAverage<double, double>& GetRenderTimeStats();
const WindowAverage<double, double>& GetFrameTimeStats();
void StartCapturingMetrics();
Metrics GetMetrics();
void PauseAutoAdjustment();
void ResumeAutoAdjustment();
int GetQualityDelayUp() const
{
return mQualityDelayUp;
}
int GetQualityDelayDown() const
{
return mQualityDelayDown;
}
int GetBackoffCounter() const
{
return mBadBackoffFrameCounter;
}
double GetBackoffAverage() const
{
return fastBackoffAverage.getStats().average;
}
protected:
bool mSSAOSupported;
bool mAdjustmentOn;
CRenderSettings* mSettings;
const RenderCaps* mRenderCaps;
bool mBlockCullingEnabled;
bool mAggressivePerformance;
int mStableFramesCounter;
bool mThrottlingOn;
int mCurrentQualityLevel;
unsigned mQualityCount[CRenderSettings::QualityLevelMax];
int mQualityDelayUp;
int mQualityDelayDown;
int mRecomputeDistanceDelay;
bool mWasQualityUp;
int mSwitchCounter;
private:
float mSqDistance;
float mSqRenderDistance;
void UpdateStats(double frameTime, double renderTime, double prepareTime);
void AdjustQuality(double frameTime, double renderTime, bool adjustmentOn, double bonusTime);
void StepQuality(bool direction, bool isBackOff);
void UpdateQualitySettings();
void SendQualityLevelStats();
float GetAvarageQuality();
float GetTargetFrameTime(int level) const;
Aya::WindowAverage<double, double> frameTimeAverage;
Aya::WindowAverage<double, double> renderTimeAverage;
Aya::WindowAverage<double, double> prepareTimeAverage;
Aya::WindowAverage<double, double> frameTimeVarianceAverage;
Aya::WindowAverage<double, double> fastBackoffAverage;
int mBadBackoffFrameCounter;
Metrics mMetrics;
Aya::Timer<Aya::Time::Fast> mSettleTimer;
bool mIsStable;
bool mIsGatheringDistance;
int mBlockCounter;
int mBlockTarget;
int mLastBlockCounter;
class AvgFpsCounter
{
public:
AvgFpsCounter()
: timeSumSec(0)
, frameCnt(0)
{
}
void Update(double deltaTimeMs)
{
if (deltaTimeMs < 1000)
{
timeSumSec += deltaTimeMs * 0.001;
++frameCnt;
}
}
double GetFPS()
{
return frameCnt ? 1.0 / (timeSumSec / frameCnt) : 0;
}
private:
double timeSumSec;
unsigned frameCnt;
};
AvgFpsCounter mFPSCounter;
THROTTLE_LOCKSTEP* LockstepTable;
};
} // namespace Aya

View File

@@ -0,0 +1,332 @@
#include "Base/GfxPart.hpp"
#include "DataModel/DataModelMesh.hpp"
#include "DataModel/Decal.hpp"
#include "DataModel/Workspace.hpp"
#include "DataModel/PartInstance.hpp"
#include "DataModel/PrismInstance.hpp"
#include "DataModel/PyramidInstance.hpp"
#include "DataModel/BasicPartInstance.hpp"
#include "DataModel/MeshPartInstance.hpp"
#include "DataModel/ExtrudedPartInstance.hpp"
#include "DataModel/PrismInstance.hpp"
#include "DataModel/PyramidInstance.hpp"
#include "DataModel/FileMesh.hpp"
#include "DataModel/SpecialMesh.hpp"
#include "DataModel/BlockMesh.hpp"
#include "DataModel/CylinderMesh.hpp"
#include "DataModel/PartOperation.hpp"
#include "Humanoid/Humanoid.hpp"
#include "World/Primitive.hpp"
#include "DataModel/PartCookie.hpp"
namespace
{
void updateCookie(Aya::PartInstance* part)
{
if (part)
part->setCookie(Aya::PartCookie::compute(part));
}
} // namespace
namespace Aya
{
GfxBinding::~GfxBinding()
{
AYAASSERT(!isBound());
}
// connects property change event listeners.
void GfxBinding::bindProperties(const shared_ptr<Aya::PartInstance>& part)
{
updateCookie(part.get());
connections.push_back(part->combinedSignal.connect(boost::bind(&GfxPart::onCombinedSignal, this, _1, _2)));
part->visitChildren(boost::bind(&GfxPart::onChildAdded, this, _1));
}
void GfxBinding::zombify()
{
unbind();
invalidateEntity();
}
void GfxBinding::unbind()
{
if (partInstance)
{
partInstance->setGfxPart(NULL);
}
for (size_t i = 0; i < connections.size(); ++i)
{
connections[i].disconnect();
}
connections.clear();
}
bool GfxBinding::isBound()
{
return !connections.empty();
}
void GfxBinding::onChildAdded(const shared_ptr<Instance>& child)
{
if (DataModelMesh* specShape = Instance::fastDynamicCast<DataModelMesh>(child.get()))
{
connections.push_back(specShape->propertyChangedSignal.connect(boost::bind(&GfxPart::onSpecialShapeChangedEx, this)));
onSpecialShapeChangedEx();
}
else if (DecalTexture* tex = Instance::fastDynamicCast<DecalTexture>(child.get()))
{
connections.push_back(tex->propertyChangedSignal.connect(boost::bind(&GfxPart::onTexturePropertyChanged, this, _1)));
updateCookie(partInstance.get());
invalidateEntity();
}
else if (Decal* decal = Instance::fastDynamicCast<Decal>(child.get()))
{
connections.push_back(decal->propertyChangedSignal.connect(boost::bind(&GfxPart::onDecalPropertyChanged, this, _1)));
updateCookie(partInstance.get());
invalidateEntity();
}
}
void GfxBinding::onChildRemoved(const shared_ptr<Instance>& child)
{
if (Instance::fastDynamicCast<DataModelMesh>(child.get()))
{
// todo: need a disconnect for propertyChangedEvents...
onSpecialShapeChangedEx();
}
else if (Instance::fastDynamicCast<DecalTexture>(child.get()))
{
// todo: need a disconnect for propertyChangedEvents...
updateCookie(partInstance.get());
invalidateEntity();
}
else if (Instance::fastDynamicCast<Decal>(child.get()))
{
// todo: need a disconnect for propertyChangedEvents...
updateCookie(partInstance.get());
invalidateEntity();
}
}
bool GfxBinding::isInWorkspace(Aya::Instance* part)
{
Instance* ws = Workspace::findWorkspace(part);
return ws && part->isDescendantOf(ws);
}
void GfxBinding::onAncestorChanged(const shared_ptr<Instance>& ancestor)
{
// Remove me from the scene if I am being removed from the Workspace
if (partInstance && !isInWorkspace(partInstance.get()))
{
// will cause a delete on next updateEntity()
zombify();
}
else
{
// part was removed from its ancestor (potentially deleted, or moved to a different cluster)
updateCookie(partInstance.get());
invalidateEntity();
}
}
void GfxBinding::onSpecialShapeChangedEx()
{
updateCookie(partInstance.get());
onSpecialShapeChanged();
}
void GfxBinding::onPropertyChanged(const Reflection::PropertyDescriptor* descriptor)
{
if (*descriptor == PartInstance::prop_CFrame)
{
onCoordinateFrameChanged();
}
else if (*descriptor == PartInstance::prop_Anchored)
{
// partInstance->getGfxPart()->onClumpChanged();
}
else if (*descriptor == PartInstance::prop_Size)
{
onSizeChanged();
}
else if (*descriptor == PartInstance::prop_Transparency || *descriptor == PartInstance::prop_LocalTransparencyModifier)
{
onTransparencyChanged();
}
else if (*descriptor == PartInstance::prop_renderMaterial)
{
invalidateEntity();
}
else if (*descriptor == PartInstance::prop_Reflectance)
{
invalidateEntity();
}
else if (*descriptor == BasicPartInstance::prop_shapeXml)
{
invalidateEntity();
}
else if (*descriptor == ExtrudedPartInstance::prop_styleXml)
{
invalidateEntity();
}
else if (*descriptor == PrismInstance::prop_sidesXML)
{
invalidateEntity();
}
// else if (*descriptor==PrismInstance::prop_slices) {
// invalidateEntity();
// }
else if (*descriptor == PyramidInstance::prop_sidesXML)
{
invalidateEntity();
}
// else if (*descriptor==PyramidInstance::prop_slices) {
// invalidateEntity();
// }
else if (Surface::isSurfaceDescriptor(*descriptor))
{
invalidateEntity();
}
else if (*descriptor == PartInstance::prop_Color)
{
invalidateEntity();
}
else if (*descriptor == PartOperation::desc_MeshData)
{
invalidateEntity();
}
else if (*descriptor == PartOperation::desc_UsePartColor)
{
invalidateEntity();
}
else if (*descriptor == PartOperation::desc_FormFactor)
{
invalidateEntity();
}
else if (*descriptor == MeshPartInstance::prop_meshPartId)
{
invalidateEntity();
}
else if (*descriptor == MeshPartInstance::prop_meshPartTextureId)
{
invalidateEntity();
}
}
void GfxBinding::onTexturePropertyChanged(const Reflection::PropertyDescriptor* descriptor)
{
if (*descriptor == FaceInstance::prop_Face)
{
updateCookie(partInstance.get());
invalidateEntity();
}
else if (*descriptor == DecalTexture::prop_Texture)
{
updateCookie(partInstance.get());
invalidateEntity();
}
else if (*descriptor == DecalTexture::prop_Specular)
invalidateEntity();
else if (*descriptor == DecalTexture::prop_Shiny)
invalidateEntity();
else if (*descriptor == DecalTexture::prop_StudsPerTileU)
invalidateEntity();
else if (*descriptor == DecalTexture::prop_StudsPerTileV)
invalidateEntity();
else if (*descriptor == DecalTexture::prop_Transparency || *descriptor == Decal::prop_LocalTransparencyModifier)
invalidateEntity();
}
void GfxBinding::onDecalPropertyChanged(const Reflection::PropertyDescriptor* descriptor)
{
if (*descriptor == FaceInstance::prop_Face)
{
updateCookie(partInstance.get());
invalidateEntity();
}
else if (*descriptor == Aya::Decal::prop_Texture)
{
updateCookie(partInstance.get());
invalidateEntity();
}
else if (*descriptor == Aya::Decal::prop_Specular)
invalidateEntity();
else if (*descriptor == Aya::Decal::prop_Shiny)
invalidateEntity();
else if (*descriptor == Aya::Decal::prop_Transparency || *descriptor == Decal::prop_LocalTransparencyModifier)
invalidateEntity();
else if (*descriptor == Aya::Decal::prop_Color3)
invalidateEntity();
}
void GfxBinding::onCombinedSignal(Instance::CombinedSignalType type, const Instance::ICombinedSignalData* data)
{
switch (type)
{
case Instance::OUTFIT_CHANGED:
onOutfitChanged();
break;
case Instance::HUMANOID_CHANGED:
onHumanoidChanged();
break;
case Instance::CHILD_ADDED:
onChildAdded(boost::polymorphic_downcast<const Instance::ChildAddedSignalData*>(data)->child);
break;
case Instance::CHILD_REMOVED:
onChildRemoved(boost::polymorphic_downcast<const Instance::ChildRemovedSignalData*>(data)->child);
break;
case Instance::ANCESTRY_CHANGED:
onAncestorChanged(boost::polymorphic_downcast<const Instance::AncestryChangedSignalData*>(data)->child);
break;
case Instance::PROPERTY_CHANGED:
onPropertyChanged(boost::polymorphic_downcast<const Instance::PropertyChangedSignalData*>(data)->propertyDescriptor);
break;
default:
break;
}
}
void GfxBinding::onOutfitChanged()
{
invalidateEntity();
}
void GfxBinding::onHumanoidChanged()
{
updateCookie(partInstance.get());
invalidateEntity();
}
void GfxBinding::cleanupStaleConnections()
{
for (size_t i = connections.size(); i != 0; --i)
{
if (!connections[i - 1].connected())
{
connections.erase(connections.begin() + (i - 1)); // remove dead connection.
}
}
}
/*override*/ void GfxAttachment::unbind()
{
// nb: specifically ignore baseclass impl. we don't want to call setGfxPart.
for (size_t i = 0; i < connections.size(); ++i)
{
connections[i].disconnect();
}
connections.clear();
}
} // namespace Aya

View File

@@ -0,0 +1,147 @@
#pragma once
#include "boost/shared_ptr.hpp"
#include "Utility/SpatialRegion.hpp"
#include "Tree/Instance.hpp"
#include "World/BasicSpatialHashPrimitive.hpp"
#include "signal.hpp"
#include "Reflection/Property.hpp"
namespace Aya
{
class PartInstance;
class AsyncResult;
class GfxBinding
{
protected:
GfxBinding(const boost::shared_ptr<Aya::PartInstance>& part)
: partInstance(part)
{
}
GfxBinding() {}
virtual ~GfxBinding();
public:
Aya::PartInstance* getPartInstance()
{
return partInstance.get();
};
// unlinks from PartInstance.
// will cause delete on next updateEntity();
void zombify();
bool isBound();
// helper method. probably should be elsewhere.
static bool isInWorkspace(Aya::Instance* part);
virtual void invalidateEntity() {};
virtual void updateEntity(bool assetsUpdated = false) {};
virtual void updateChunk(const SpatialRegion::Id& pos, bool isWaterChunk) {};
virtual void onCoordinateFrameChanged() {};
virtual void onSizeChanged()
{
invalidateEntity();
};
virtual void onTransparencyChanged()
{
invalidateEntity();
};
virtual void onSpecialShapeChanged()
{
invalidateEntity();
}
// disconnects all event listeners.
virtual void unbind();
void cleanupStaleConnections();
// meant to connect all listeners relevant to this instance.
// basic implementation doesn't listen to much. overide and bind some more.
// virtual void bind();
// helper: connects property change event listeners.
void bindProperties(const shared_ptr<Aya::PartInstance>& part);
protected:
boost::shared_ptr<Aya::PartInstance> partInstance;
std::vector<Aya::signals::connection> connections;
private:
void onPropertyChanged(const Aya::Reflection::PropertyDescriptor* descriptor);
void onAncestorChanged(const shared_ptr<Aya::Instance>& ancestor);
void onChildAdded(const shared_ptr<Aya::Instance>& child);
void onChildRemoved(const shared_ptr<Aya::Instance>& child);
void onSpecialShapeChangedEx();
void onCombinedSignal(Instance::CombinedSignalType type, const Instance::ICombinedSignalData* data);
void onHumanoidChanged();
void onOutfitChanged();
void onDecalPropertyChanged(const Aya::Reflection::PropertyDescriptor* descriptor);
void onTexturePropertyChanged(const Aya::Reflection::PropertyDescriptor* descriptor);
};
// class used as a simple base class for linking PartInstances with graphics objects.
class GfxPart
: public GfxBinding
, public Aya::BasicSpatialHashPrimitive
{
public:
GfxPart(const boost::shared_ptr<Aya::PartInstance>& part)
: GfxBinding(part)
, lastFrustumVisibleFrameNumber(-1)
{
}
GfxPart()
: lastFrustumVisibleFrameNumber(-1)
{
}
// accessors?
int lastFrustumVisibleFrameNumber; // most recent frame where this object was within the view frustum
public:
virtual void updateCoordinateFrame(bool recalcLocalBounds = false) {};
virtual unsigned int getPartCount()
{
return 1;
}
virtual void onSleepingChanged(bool sleeping, PartInstance* part) {};
virtual void onClumpChanged(PartInstance* part) {};
virtual Vector3 getCenter() const
{
return Vector3();
}
};
// serves to allow the gfx engine to have persistent gfxobject tracking the position of a part.
class GfxAttachment : public GfxBinding
{
public:
GfxAttachment(const boost::shared_ptr<Aya::PartInstance>& part)
: GfxBinding(part)
{
}
protected:
GfxAttachment() {}
public:
/*override*/ void unbind();
protected:
virtual void onSleepingChanged(bool sleeping) = 0;
public:
virtual void updateCoordinateFrame(bool recalcLocalBounds = false) = 0;
};
} // namespace Aya

View File

@@ -0,0 +1,110 @@
#pragma once
#include "Utility/IndexArray.hpp"
#include "Utility/Selectable.hpp"
#include "Tree/Instance.hpp"
#include "SelectState.hpp"
namespace Aya
{
class IAdornableCollector;
class Adorn;
class Camera;
class AyaInterface IAdornable : public Selectable
{
friend class IAdornableCollector;
private:
int index2d;
int index3d;
int index3dSorted;
int& indexFunc2d()
{
return index2d;
}
int& indexFunc3d()
{
return index3d;
}
int& indexFunc3dSorted()
{
return index3dSorted;
}
IAdornableCollector* bucket;
protected:
virtual bool shouldRender2d() const
{
return false;
}
virtual bool shouldRender3dAdorn() const
{
return false;
}
virtual bool shouldRender3dSortedAdorn() const
{
return false;
}
public:
IAdornable()
: bucket(NULL)
, index2d(-1)
, index3d(-1)
, index3dSorted(-1)
{
}
~IAdornable();
void shouldRenderSetDirty(); // sets this IAdornable dirty
float calculateDepth(const Camera* camera) const; // calculates the depth based on camera
virtual bool isVisible(const Rect2D& rect) const
{
return true;
}
virtual void renderBackground2d(Adorn* adorn) {}
virtual void renderBackground2dContext(Adorn* adorn, const Instance* context)
{
renderBackground2d(adorn);
}
virtual void render2d(Adorn* adorn) {}
virtual void render2dContext(Adorn* adorn, const Instance* context)
{
render2d(adorn);
}
virtual void render3dAdorn(Adorn* adorn) {}
virtual void render3dSortedAdorn(Adorn* adorn) {}
virtual void render3dSelect(Adorn* adorn, SelectState selectState) {}
virtual Vector3 render3dSortedPosition() const
{
return Vector3(0, 0, 0);
}
virtual int getDisplayOrder() const
{
return 0;
}
};
struct AdornableDepth
{
IAdornable* adornable;
float depth;
bool operator<(const AdornableDepth& o) const
{
return depth > o.depth;
}
};
} // namespace Aya

View File

@@ -0,0 +1,210 @@
#include "Base/IAdornableCollector.hpp"
#include "DataModel/Camera.hpp"
#include "Debug.hpp"
#include "FastLog.hpp"
#include <algorithm>
#include <vector>
#include <utility>
LOGGROUP(AdornableLifetime);
DYNAMIC_FASTFLAGVARIABLE(DontReorderScreenGuisWhenDescendantRemoving, false)
namespace Aya
{
IAdornable::~IAdornable()
{
FASTLOG1(FLog::AdornableLifetime, "Destroying adornable %p", this);
if (bucket)
bucket->onRenderableDescendantRemoving(this);
}
void IAdornable::shouldRenderSetDirty()
{
if (bucket)
{
bucket->recomputeShouldRender(this);
}
}
float IAdornable::calculateDepth(const Camera* camera) const
{
return camera->dot(render3dSortedPosition());
}
//////////////////////////////////////////////////////////////
IAdornableCollector::~IAdornableCollector()
{
FASTLOG1(FLog::AdornableLifetime, "Adornable Collector %p deleted", this);
FASTLOG3(
FLog::AdornableLifetime, "Renderables 2D: %u 3D: %u 3DSorted: %u", renderable2ds.size(), renderable3ds.size(), renderable3dSorteds.size());
AYAASSERT(renderable2ds.size() == 0);
AYAASSERT(renderable3ds.size() == 0);
AYAASSERT(renderable3dSorteds.size() == 0);
}
void IAdornableCollector::recomputeShouldRender(IAdornable* iR)
{
AYAASSERT(iR->bucket == this);
if (iR->shouldRender2d())
{
if (!renderable2ds.fastContains(iR))
{
FASTLOG2(FLog::AdornableLifetime, "Collector %p: Adding 2D adorn %p", this, iR);
renderable2ds.fastAppend(iR);
}
}
else
{
if (renderable2ds.fastContains(iR))
{
FASTLOG2(FLog::AdornableLifetime, "Collector %p: Removing 2D adorn %p", this, iR);
renderable2ds.fastRemove(iR);
}
}
if (iR->shouldRender3dAdorn())
{
if (!renderable3ds.fastContains(iR))
{
FASTLOG2(FLog::AdornableLifetime, "Collector %p: Adding 3D adorn %p", this, iR);
renderable3ds.fastAppend(iR);
}
}
else
{
if (renderable3ds.fastContains(iR))
{
FASTLOG2(FLog::AdornableLifetime, "Collector %p: Removing 3D adorn %p", this, iR);
renderable3ds.fastRemove(iR);
}
}
if (iR->shouldRender3dSortedAdorn())
{
if (!renderable3dSorteds.fastContains(iR))
{
FASTLOG2(FLog::AdornableLifetime, "Collector %p: Adding 3DSort adorn %p", this, iR);
renderable3dSorteds.fastAppend(iR);
}
}
else
{
if (renderable3dSorteds.fastContains(iR))
{
FASTLOG2(FLog::AdornableLifetime, "Collector %p: Removing 3DSort adorn %p", this, iR);
renderable3dSorteds.fastRemove(iR);
}
}
}
void IAdornableCollector::onRenderableDescendantAdded(IAdornable* iR)
{
AYAASSERT(iR->index2d == -1);
AYAASSERT(iR->index3d == -1);
AYAASSERT(iR->index3dSorted == -1);
AYAASSERT(iR->bucket == NULL);
iR->bucket = this;
recomputeShouldRender(iR);
}
void IAdornableCollector::onRenderableDescendantRemoving(IAdornable* iR)
{
AYAASSERT(iR->bucket == this);
if (renderable2ds.fastContains(iR))
{
FASTLOG2(FLog::AdornableLifetime, "Collector %p: Removing 2D adorn %p", this, iR);
if (DFFlag::DontReorderScreenGuisWhenDescendantRemoving)
{
renderable2ds.remove(iR);
}
else
{
renderable2ds.fastRemove(iR);
}
}
if (renderable3ds.fastContains(iR))
{
FASTLOG2(FLog::AdornableLifetime, "Collector %p: Removing 3D adorn %p", this, iR);
renderable3ds.fastRemove(iR);
}
if (renderable3dSorteds.fastContains(iR))
{
FASTLOG2(FLog::AdornableLifetime, "Collector %p: Removing 3DSort adorn %p", this, iR);
renderable3dSorteds.fastRemove(iR);
}
iR->bucket = NULL;
AYAASSERT(iR->index2d == -1);
AYAASSERT(iR->index3d == -1);
AYAASSERT(iR->index3dSorted == -1);
}
void IAdornableCollector::render2dItems(Adorn* adorn)
{
FASTLOG1(FLog::AdornRenderStats, "Rendering 2D Adorn Items, %u items", renderable2ds.size());
// Create a sorted list of renderables with their display order
std::vector<std::pair<int, IAdornable*>> sortedRenderables;
sortedRenderables.reserve(renderable2ds.size());
for (int i = 0; i < renderable2ds.size(); ++i)
{
IAdornable* adornable = renderable2ds[i];
int displayOrder = adornable->getDisplayOrder();
sortedRenderables.push_back(std::make_pair(displayOrder, adornable));
}
// Sort by display order (lower values render first)
std::sort(sortedRenderables.begin(), sortedRenderables.end(),
[](const std::pair<int, IAdornable*>& a, const std::pair<int, IAdornable*>& b)
{
return a.first < b.first;
});
// Render in sorted order
for (size_t i = 0; i < sortedRenderables.size(); ++i)
{
sortedRenderables[i].second->render2d(adorn);
}
}
void IAdornableCollector::render3dAdornItems(Adorn* adorn)
{
FASTLOG1(FLog::AdornRenderStats, "Rendering 3D Adorn Items, %u items", renderable3ds.size());
for (int i = 0; i < renderable3ds.size(); ++i)
{
renderable3ds[i]->render3dAdorn(adorn);
}
}
void IAdornableCollector::append3dSortedAdornItems(std::vector<AdornableDepth>& destination, const Camera* camera) const
{
FASTLOG1(FLog::AdornRenderStats, "Rendering 3DSort Adorn Items, %u items", renderable3dSorteds.size());
for (int i = 0; i < renderable3dSorteds.size(); ++i)
{
AdornableDepth ad = {renderable3dSorteds[i], renderable3dSorteds[i]->calculateDepth(camera)};
destination.push_back(ad);
}
}
} // namespace Aya

View File

@@ -0,0 +1,39 @@
#pragma once
#include "Base/IAdornable.hpp"
#include "Utility/IndexArray.hpp"
LOGGROUP(AdornRenderStats);
namespace Aya
{
class Adorn;
class AyaInterface IAdornableCollector
{
friend class IAdornable;
private:
IndexArray<IAdornable, &IAdornable::indexFunc2d> renderable2ds;
IndexArray<IAdornable, &IAdornable::indexFunc3d> renderable3ds;
IndexArray<IAdornable, &IAdornable::indexFunc3dSorted> renderable3dSorteds;
public:
void onRenderableDescendantAdded(IAdornable* iR);
void onRenderableDescendantRemoving(IAdornable* iR);
void recomputeShouldRender(IAdornable* iR);
public:
IAdornableCollector() {}
~IAdornableCollector();
void render2dItems(Adorn* adorn);
void render3dAdornItems(Adorn* adorn);
void append3dSortedAdornItems(std::vector<AdornableDepth>& destination, const Camera* camera) const;
};
} // namespace Aya

View File

@@ -0,0 +1,19 @@
#pragma once
#include <stddef.h>
namespace Aya
{
class Image
{
public:
virtual ~Image() {}
virtual size_t getSize() const = 0;
virtual int getOriginalWidth() const = 0;
virtual int getOriginalHeight() const = 0;
};
} // namespace Aya

View File

@@ -0,0 +1,36 @@
#pragma once
namespace Aya
{
#pragma pack(push, 1)
// nb: keep backward/forward compatibility by only appending to these structs.
// stride information will keep this working.
struct FileMeshHeader
{
unsigned short cbSize;
unsigned char cbVerticesStride;
unsigned char cbFaceStride;
// ---dword boundary-----
unsigned int num_vertices;
unsigned int num_faces;
};
struct FileMeshVertexNormalTexture3d
{
float vx, vy, vz;
float nx, ny, nz;
float tu, tv, tw;
};
struct FileMeshFace
{
unsigned int a;
unsigned int b;
unsigned int c;
};
#pragma pack(pop)
} // namespace Aya

View File

@@ -0,0 +1,20 @@
#pragma once
#include "Utility/G3DCore.hpp"
namespace Aya
{
class I3DLinearFunc
{
public:
virtual Vector3 eval(float t) = 0;
// first derivative.
virtual Vector3 evalTangent(float t) = 0; // (tangent, normal, binormal, in that order, should form a right handed space)
virtual Vector3 evalNormal(float t) = 0;
virtual Vector3 evalBinormal(float t) = 0;
// string that encodes this function in a unique way.
virtual std::string hashString() = 0;
};
} // namespace Aya

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,66 @@
#pragma once
#include "Utility/SurfaceType.hpp"
#include "Utility/Vector6.hpp"
#include "Vector3.hpp"
#include "Color4.hpp"
#include "CoordinateFrame.hpp"
// Simple description of a part suitable for drawing, etc. Build Instance on top of this.
// Low level.
namespace Aya
{
enum PartType
{
BALL_PART = 0,
BLOCK_PART,
CYLINDER_PART,
TRUSS_PART,
WEDGE_PART,
PRISM_PART,
PYRAMID_PART,
PARALLELRAMP_PART,
RIGHTANGLERAMP_PART,
CORNERWEDGE_PART,
MEGACLUSTER_PART,
OPERATION_PART,
MESH_PART
};
class Part
{
public:
// alpha order for simplification on dialogs
PartType type; // hash code hashes this block of data
G3D::Vector3 gridSize;
G3D::Color4 color;
Vector6<SurfaceType> surfaceType;
G3D::CoordinateFrame coordinateFrame;
Part() {}
Part(PartType _type, const G3D::Vector3& _gridSize, const G3D::Color4 _color, const G3D::CoordinateFrame& c)
: type(_type)
, gridSize(_gridSize)
, color(_color)
, surfaceType(NO_SURFACE)
, coordinateFrame(c)
{
}
Part(PartType type, const G3D::Vector3& gridSize, const G3D::Color4 color, const Vector6<SurfaceType>& surfaceType, const G3D::CoordinateFrame& c)
: type(type)
, gridSize(gridSize)
, color(color)
, surfaceType(surfaceType)
, coordinateFrame(c)
{
}
};
} // namespace Aya

View File

@@ -0,0 +1,328 @@
#include "Base/PartIdentifier.hpp"
#include "Humanoid/Humanoid.hpp"
#include "DataModel/DataModelMesh.hpp"
#include "DataModel/FileMesh.hpp"
#include "DataModel/Decal.hpp"
#include "DataModel/Workspace.hpp"
#include "DataModel/PartInstance.hpp"
#include "DataModel/CharacterAppearance.hpp"
#include "DataModel/CharacterMesh.hpp"
#include "DataModel/Accoutrement.hpp"
#include "DataModel/MeshPartInstance.hpp"
#include "DataModel/PartCookie.hpp"
namespace Aya
{
static const Vector3 humanoidPartScalesR15[HumanoidIdentifier::PartType_Count] = {
Vector3(2, 1.6, 1), // PartType_UpperTorso
Vector3(2, 0.4, 1), // PartType_LowerTorso
Vector3(1, 1.2, 1), // PartType_LowerArm
Vector3(1, 1.4, 1), // PartType_UpperArm
Vector3(1, 0.3, 1), // PartType_Hand
Vector3(1, 1.5, 1), // PartType_UpperLeg
Vector3(1, 1.5, 1), // PartType_LowerLeg
Vector3(1, 0.3, 1) // PartType_Foot
};
static const Vector3 humanoidPartScales[HumanoidIdentifier::PartType_Count] = {
Vector3(1, 1, 1), // PartType_Head
Vector3(2, 2, 1), // PartType_Torso
Vector3(1, 2, 1), // PartType_Arm
Vector3(1, 2, 1), // PartType_Leg
Vector3(1, 1, 1) // PartType_Unknown
};
// if the part is a humanoid, get further details with this.
HumanoidIdentifier::HumanoidIdentifier(Aya::Humanoid* humanoid)
: humanoid(humanoid)
, head(0)
, leftLeg(0)
, rightLeg(0)
, leftArm(0)
, rightArm(0)
, torso(0)
, upperTorso(0)
, lowerTorso(0)
, rightUpperArm(0)
, rightLowerArm(0)
, rightHand(0)
, leftUpperArm(0)
, leftLowerArm(0)
, leftHand(0)
, rightUpperLeg(0)
, rightLowerLeg(0)
, rightFoot(0)
, leftUpperLeg(0)
, leftLowerLeg(0)
, leftFoot(0)
, leftLegMesh(0)
, rightLegMesh(0)
, leftArmMesh(0)
, rightArmMesh(0)
, torsoMesh(0)
{
if (!humanoid)
return;
Instance* parent = humanoid->getParent();
if (!parent)
return;
const Instances& children = *parent->getChildren();
for (size_t i = 0; i < children.size(); ++i)
{
Instance* inst = children[i].get();
if (PartInstance* part = Instance::fastDynamicCast<PartInstance>(inst))
{
const std::string& name = part->getName();
if (name == "Head")
head = part;
else if (name == "Left Leg")
leftLeg = part;
else if (name == "Right Leg")
rightLeg = part;
else if (name == "Left Arm")
leftArm = part;
else if (name == "Right Arm")
rightArm = part;
else if (name == "Torso")
torso = part;
if (name == "UpperTorso")
upperTorso = part;
else if (name == "LowerTorso")
lowerTorso = part;
else if (name == "LeftUpperArm")
leftUpperArm = part;
else if (name == "LeftLowerArm")
leftLowerArm = part;
else if (name == "LeftHand")
leftHand = part;
else if (name == "RightUpperArm")
rightUpperArm = part;
else if (name == "RightLowerArm")
rightLowerArm = part;
else if (name == "RightHand")
rightHand = part;
else if (name == "LeftUpperLeg")
leftUpperLeg = part;
else if (name == "LeftLowerLeg")
leftLowerLeg = part;
else if (name == "LeftFoot")
leftFoot = part;
else if (name == "RightUpperLeg")
rightUpperLeg = part;
else if (name == "RightLowerLeg")
rightLowerLeg = part;
else if (name == "RightFoot")
rightFoot = part;
}
else if (Clothing* c = Instance::fastDynamicCast<Clothing>(inst))
{
if (pants.isNull() && !c->outfit1.isNull())
pants = c->outfit1;
if (shirt.isNull() && !c->outfit2.isNull())
shirt = c->outfit2;
}
else if (ShirtGraphic* s = Instance::fastDynamicCast<ShirtGraphic>(inst))
{
if (shirtGraphic.isNull() && !s->graphic.isNull())
shirtGraphic = s->graphic;
}
else if (CharacterMesh* m = Instance::fastDynamicCast<CharacterMesh>(inst))
{
switch (m->getBodyPart())
{
case CharacterMesh::LEFTARM:
leftArmMesh = m;
break;
case CharacterMesh::RIGHTARM:
rightArmMesh = m;
break;
case CharacterMesh::LEFTLEG:
leftLegMesh = m;
break;
case CharacterMesh::RIGHTLEG:
rightLegMesh = m;
break;
case CharacterMesh::TORSO:
torsoMesh = m;
break;
default:
AYAASSERT(!"Unsupported body part type");
break;
}
}
else if (Accoutrement* a = Instance::fastDynamicCast<Accoutrement>(inst))
{
accoutrements.push_back(a);
}
}
}
CharacterMesh* HumanoidIdentifier::getRelevantMesh(Aya::PartInstance* bodyPart) const
{
if (bodyPart == leftLeg || bodyPart == leftUpperLeg || bodyPart == leftLowerLeg || bodyPart == leftFoot)
return leftLegMesh;
if (bodyPart == rightLeg || bodyPart == rightUpperLeg || bodyPart == rightLowerLeg || bodyPart == rightFoot)
return rightLegMesh;
if (bodyPart == leftArm || bodyPart == leftUpperArm || bodyPart == leftLowerArm || bodyPart == leftHand)
return leftArmMesh;
if (bodyPart == rightArm || bodyPart == rightUpperArm || bodyPart == rightLowerArm || bodyPart == rightHand)
return rightArmMesh;
if (bodyPart == torso || bodyPart == upperTorso || bodyPart == lowerTorso)
return torsoMesh;
return NULL;
}
HumanoidIdentifier::BodyPartType HumanoidIdentifier::getBodyPartType(Aya::PartInstance* bodyPart) const
{
if (humanoid->getUseR15())
{
if (bodyPart == leftUpperLeg || bodyPart == rightUpperLeg)
return PartType_UpperLeg;
if (bodyPart == leftUpperArm || bodyPart == rightUpperArm)
return PartType_UpperArm;
if (bodyPart == leftLowerLeg || bodyPart == rightLowerLeg)
return PartType_LowerLeg;
if (bodyPart == leftLowerArm || bodyPart == rightLowerArm)
return PartType_LowerArm;
if (bodyPart == leftHand || bodyPart == rightHand)
return PartType_Hand;
if (bodyPart == leftFoot || bodyPart == rightFoot)
return PartType_Foot;
if (bodyPart == upperTorso)
return PartType_UpperTorso;
if (bodyPart == lowerTorso)
return PartType_LowerTorso;
}
else
{
if (bodyPart == leftLeg || bodyPart == rightLeg)
return PartType_Leg;
if (bodyPart == leftArm || bodyPart == rightArm)
return PartType_Arm;
if (bodyPart == torso)
return PartType_Torso;
if (bodyPart == head)
return PartType_Head;
}
return PartType_Unknown;
}
Vector3 HumanoidIdentifier::getBodyPartScale(Aya::PartInstance* bodyPart) const
{
return humanoidPartScales[getBodyPartType(bodyPart)];
}
bool HumanoidIdentifier::isPartHead(Aya::PartInstance* part) const
{
if (part != head)
return false;
if (Aya::DataModelMesh* specialShape = Aya::getSpecialShape(part))
{
bool hasFace = (part->getCookie() & PartCookie::HAS_DECALS) != 0;
if (Aya::SpecialShape* shape = specialShape->fastDynamicCast<Aya::SpecialShape>())
{
// A real head or a file mesh - treat it as a head even if there is no face (might use a mesh texture)
if (shape->getMeshType() == Aya::SpecialShape::HEAD_MESH || shape->getMeshType() == Aya::SpecialShape::FILE_MESH)
return true;
// Probably one of the heads from the store - all character heads have faces
if (shape->getMeshType() == Aya::SpecialShape::SPHERE_MESH)
return hasFace;
// Unrecognized shape type - this is not a head from the store, so don't treat it as a head.
return false;
}
else if (specialShape->fastDynamicCast<Aya::FileMesh>())
{
// A file mesh - treat it as a head even if there is no face (might use a mesh texture)
return true;
}
else
{
// Probably one of the heads from the store - all character heads have faces
return hasFace;
}
}
// No special shape - should be a simple part
return false;
}
bool HumanoidIdentifier::isBodyPart(Aya::PartInstance* part) const
{
if (humanoid->getUseR15())
return (leftUpperArm == part || leftUpperLeg == part || rightUpperArm == part || rightUpperLeg == part || upperTorso == part ||
lowerTorso == part || rightLowerArm == part || leftLowerArm == part || leftHand == part || rightHand == part || leftFoot == part ||
rightFoot == part || leftLowerLeg == part || rightLowerLeg == part || head == part);
else if (part->getCookie() & PartCookie::IS_HUMANOID_PART)
return (leftArm == part || leftLeg == part || rightArm == part || rightLeg == part || torso == part || head == part);
else
return false;
}
bool HumanoidIdentifier::isBodyPartComposited(Aya::PartInstance* part) const
{
bool noReflectance = part->getReflectance() <= 0.015f;
if (MeshPartInstance* meshPart = part->fastDynamicCast<MeshPartInstance>())
return humanoid->getUseR15();
// Heads are always composited as long as they are not transparent
if (part == head)
return humanoid->getUseR15() ? false : (part->getTransparencyUi() <= 0 && isPartHead(part) && noReflectance);
// Body parts with special shapes are never composited to match the old behavior
if (part->getCookie() & PartCookie::HAS_SPECIALSHAPE)
return false;
// Non-block parts are never composited to match the old behavior
if (part->getPartType() != BLOCK_PART)
return false;
// Parts with meshes are always composited
if (getRelevantMesh(part))
return true;
// Arms are always composited if there is a shirt
if ((part == leftArm || part == rightArm) && !shirt.isNull())
return true;
// Legs are always composited if there are pants
if ((part == leftLeg || part == rightLeg) && !pants.isNull())
return true;
// Torso is always composited if there is a shirt, pants or a t-shirt
if (part == torso && (!pants.isNull() || !shirt.isNull() || !shirtGraphic.isNull()))
return true;
// Now we have a body part and we have a choice - we can composit it or skip compositing.
// Compositing means that we lose materials; we also replace the body part with a prebaked mesh, so we lose the size information.
// We also lose studs, but we care way less about those - so to improve batching, we'll composit plastic parts with expected size.
return (part->getRenderMaterial() == PLASTIC_MATERIAL || part->getRenderMaterial() == SMOOTH_PLASTIC_MATERIAL) && noReflectance;
}
bool HumanoidIdentifier::isPartComposited(Aya::PartInstance* part) const
{
if (isBodyPart(part))
return isBodyPartComposited(part);
// We don't composit Accoutrements for R15
// @mdolli
if (Instance::isA<Accoutrement>(part->getParent()) && !this->humanoid->getUseR15())
return true;
return false;
}
} // namespace Aya

View File

@@ -0,0 +1,100 @@
#pragma once
#include "Utility/TextureId.hpp"
#include "Utility/G3DCore.hpp"
namespace Aya
{
class PartInstance;
class PartInstance;
class Humanoid;
class CharacterMesh;
class Accoutrement;
// if the part is a humanoid, get further details with this.
class HumanoidIdentifier
{
public:
explicit HumanoidIdentifier(Aya::Humanoid* humanoid);
Humanoid* humanoid;
PartInstance* head;
PartInstance* leftLeg;
PartInstance* rightLeg;
PartInstance* leftArm;
PartInstance* rightArm;
PartInstance* torso;
PartInstance* upperTorso;
PartInstance* lowerTorso;
PartInstance* leftUpperArm;
PartInstance* leftLowerArm;
PartInstance* leftHand;
PartInstance* rightUpperArm;
PartInstance* rightLowerArm;
PartInstance* rightHand;
PartInstance* leftUpperLeg;
PartInstance* leftLowerLeg;
PartInstance* leftFoot;
PartInstance* rightUpperLeg;
PartInstance* rightLowerLeg;
PartInstance* rightFoot;
std::vector<Accoutrement*> accoutrements;
TextureId pants;
TextureId shirt;
TextureId shirtGraphic;
CharacterMesh* leftLegMesh;
CharacterMesh* rightLegMesh;
CharacterMesh* leftArmMesh;
CharacterMesh* rightArmMesh;
CharacterMesh* torsoMesh;
CharacterMesh* leftUpperLegMesh;
CharacterMesh* leftLowerLegMesh;
CharacterMesh* leftFootMesh;
CharacterMesh* rightUpperLegMesh;
CharacterMesh* rightLowerLegMesh;
CharacterMesh* rightFootMesh;
CharacterMesh* leftUpperArmMesh;
CharacterMesh* leftLowerArmMesh;
CharacterMesh* leftHandMesh;
CharacterMesh* rightUpperArmMesh;
CharacterMesh* rightLowerArmMesh;
CharacterMesh* rightHandMesh;
CharacterMesh* upperTorsoMesh;
CharacterMesh* lowerTorsoMesh;
bool isBodyPart(Aya::PartInstance* part) const;
bool isBodyPartComposited(Aya::PartInstance* part) const;
bool isPartComposited(Aya::PartInstance* part) const;
bool isPartHead(Aya::PartInstance* part) const;
// helper
CharacterMesh* getRelevantMesh(Aya::PartInstance* bodyPart) const;
enum BodyPartType
{
PartType_Head,
PartType_Torso,
PartType_Arm,
PartType_Leg,
PartType_UpperTorso,
PartType_LowerTorso,
PartType_LowerArm,
PartType_UpperArm,
PartType_Hand,
PartType_UpperLeg,
PartType_LowerLeg,
PartType_Foot,
PartType_Unknown,
PartType_Count
};
BodyPartType getBodyPartType(Aya::PartInstance* bodyPart) const;
Vector3 getBodyPartScale(Aya::PartInstance* bodyPart) const;
};
} // namespace Aya

View File

@@ -0,0 +1,16 @@
#include "Base/RenderCaps.hpp"
#include "FastLog.hpp"
namespace Aya
{
RenderCaps::RenderCaps(std::string gfxCardName, size_t vidMemSize)
: gfxCardName(gfxCardName)
, vidMemSize(vidMemSize)
, texturePowerOf2Only(false)
, supportsGBuffer(false)
, skinningBoneCount(0)
{
}
} // namespace Aya

View File

@@ -0,0 +1,58 @@
#pragma once
#include <string>
#include "Base/RenderSettings.hpp"
namespace Aya
{
class RenderCaps
{
size_t vidMemSize;
std::string gfxCardName;
bool texturePowerOf2Only;
bool supportsGBuffer;
unsigned int skinningBoneCount;
public:
RenderCaps(std::string gfxCardName, size_t vidMemSize);
void setTexturePowerOf2Only(bool b)
{
texturePowerOf2Only = b;
}
void setSupportsGBuffer(bool b)
{
supportsGBuffer = b;
}
void setSkinningBoneCount(unsigned int v)
{
skinningBoneCount = v;
}
size_t getVidMemSize() const
{
return vidMemSize;
}
bool getTexturePowerOf2Only() const
{
return texturePowerOf2Only;
}
const std::string& getGfxCardName() const
{
return gfxCardName;
}
bool getSupportsGBuffer() const
{
return supportsGBuffer;
}
unsigned int getSkinningBoneCount() const
{
return skinningBoneCount;
}
};
} // namespace Aya

View File

@@ -0,0 +1,63 @@
#include "Base/RenderSettings.hpp"
#include "Debug.hpp"
namespace Aya
{
const G3D::Vector2int16 CRenderSettings::minGameWindowSize = G3D::Vector2int16((G3D::int16)816, (G3D::int16)638);
CRenderSettings::GraphicsMode CRenderSettings::latchedGraphicsMode = CRenderSettings::UnknownGraphicsMode;
CRenderSettings::AASamples CRenderSettings::aaSamples(defaultAASamples);
const CRenderSettings::RESOLUTIONENTRY ResolutionTable[] = {{CRenderSettings::Resolution720x526, 720, 526},
{CRenderSettings::Resolution800x600, 800, 600}, {CRenderSettings::Resolution1024x600, 1024, 600},
{CRenderSettings::Resolution1024x768, 1024, 768}, {CRenderSettings::Resolution1280x720, 1280, 720},
{CRenderSettings::Resolution1280x768, 1280, 768}, {CRenderSettings::Resolution1152x864, 1152, 864},
{CRenderSettings::Resolution1280x800, 1280, 800}, {CRenderSettings::Resolution1360x768, 1360, 768},
{CRenderSettings::Resolution1280x960, 1280, 960}, {CRenderSettings::Resolution1280x1024, 1280, 1024},
{CRenderSettings::Resolution1440x900, 1440, 900}, {CRenderSettings::Resolution1600x900, 1600, 900},
{CRenderSettings::Resolution1600x1024, 1600, 1024}, {CRenderSettings::Resolution1600x1200, 1600, 1200},
{CRenderSettings::Resolution1680x1050, 1680, 1050}, {CRenderSettings::Resolution1920x1080, 1920, 1080},
{CRenderSettings::Resolution1920x1200, 1920, 1200}};
CRenderSettings::CRenderSettings()
#if !AYA_PLATFORM_IOS
: fullscreenSize(G3D::Vector2int16(800, 600)) // these are just fail safes in case auto detect procedure fails.
, windowSize(G3D::Vector2int16(800, 600)) //
#else
: fullscreenSize(G3D::Vector2int16(1024, 768)) // these are just fail safes in case auto detect procedure fails.
, windowSize(G3D::Vector2int16(1024, 768)) //
#endif
, graphicsMode(AutoGraphicsMode)
, qualityLevel(QualityAuto)
, editQualityLevel(QualityAuto)
, antialiasingMode(AntialiasingOff)
, frameRateManagerMode(FrameRateManagerAuto)
, showAggregation(false)
, drawConnectors(false)
, minCullDistance(50)
, debugShowBoundingBoxes(false) // debug.
, debugReloadAssets(false) // debug.
, objExportMergeByMaterial(false)
, eagerBulkExecution(false) // debug
, enableFRM(true)
, autoQualityLevel(1)
, resolutionPreference(ResolutionAuto)
, maxQualityLevel(QualityLevelMax)
, textureCacheSize(1024 * 1024 * 32) // 32 MB
, meshCacheSize(1024 * 1024 * 32) // 32 MB
{
}
const CRenderSettings::RESOLUTIONENTRY& CRenderSettings::getResolutionPreset(ResolutionPreset preset) const
{
AYAASSERT(preset < ResolutionMaxIndex);
AYAASSERT(ResolutionTable[preset - 1].preset == preset);
AYAASSERT(ResolutionMaxIndex == ARRAYSIZE(ResolutionTable) + 1);
return ResolutionTable[preset - 1];
}
} // namespace Aya

View File

@@ -0,0 +1,279 @@
#pragma once
#include <vector>
#include "DataModel/GameBasicSettings.hpp"
#include "Utility/G3DCore.hpp"
#include "Utility/StandardOut.hpp"
namespace Aya
{
class CRenderSettings
{
public:
enum AASamples
{
NONE = 1,
AA4 = 4,
AA8 = 8,
};
static const AASamples defaultAASamples = NONE;
static const G3D::Vector2int16 defaultWindowSize;
static const G3D::Vector2int16 defaultFullscreenSize();
static const G3D::Vector2int16 minGameWindowSize;
typedef enum
{
UnknownGraphicsMode = 0,
AutoGraphicsMode = 1,
BGFX = 2,
OpenGL,
NoGraphics
} GraphicsMode;
static GraphicsMode latchedGraphicsMode;
typedef enum
{
AntialiasingAuto = 0,
AntialiasingOn = 1,
AntialiasingOff = 2
} AntialiasingMode;
typedef enum
{
FrameRateManagerAuto = 0,
FrameRateManagerOn = 1,
FrameRateManagerOff = 2
} FrameRateManagerMode;
typedef enum
{
QualityAuto = 0,
QualityLevel1,
QualityLevel2,
QualityLevel3,
QualityLevel4,
QualityLevel5,
QualityLevel6,
QualityLevel7,
QualityLevel8,
QualityLevel9,
QualityLevel10,
QualityLevel11,
QualityLevel12,
QualityLevel13,
QualityLevel14,
QualityLevel15,
QualityLevel16,
QualityLevel17,
QualityLevel18,
QualityLevel19,
QualityLevel20,
QualityLevel21,
QualityLevelMax
} QualityLevel;
typedef enum
{
ResolutionAuto,
Resolution720x526,
Resolution800x600,
Resolution1024x600,
Resolution1024x768,
Resolution1280x720,
Resolution1280x768,
Resolution1152x864,
Resolution1280x800,
Resolution1360x768,
Resolution1280x960,
Resolution1280x1024,
Resolution1440x900,
Resolution1600x900,
Resolution1600x1024,
Resolution1600x1200,
Resolution1680x1050,
Resolution1920x1080,
Resolution1920x1200,
ResolutionMaxIndex
} ResolutionPreset;
struct RESOLUTIONENTRY
{
CRenderSettings::ResolutionPreset preset;
int width;
int height;
};
protected:
GraphicsMode graphicsMode;
AntialiasingMode antialiasingMode;
FrameRateManagerMode frameRateManagerMode;
QualityLevel qualityLevel;
QualityLevel editQualityLevel;
ResolutionPreset resolutionPreference;
int autoQualityLevel;
int maxQualityLevel;
int minCullDistance;
bool debugShowBoundingBoxes;
bool debugReloadAssets;
bool enableFRM;
bool objExportMergeByMaterial;
static AASamples aaSamples;
// filtered setting to use by app.
G3D::Vector2int16 fullscreenSize;
G3D::Vector2int16 windowSize;
bool showAggregation;
bool drawConnectors;
bool eagerBulkExecution;
// KB
unsigned int textureCacheSize;
unsigned int meshCacheSize;
public:
CRenderSettings();
bool getShowAggregation() const
{
return showAggregation;
}
static AASamples getAASamplesSafe()
{
return aaSamples;
} // Thread-safe
GraphicsMode getGraphicsMode() const
{
return graphicsMode;
}
void setGraphicsMode(GraphicsMode value);
GraphicsMode getLatchedGraphicsMode()
{
if (latchedGraphicsMode == UnknownGraphicsMode)
latchedGraphicsMode = getGraphicsMode();
return latchedGraphicsMode;
}
AASamples getAASamples() const
{
return aaSamples;
}
G3D::Vector2int16 getFullscreenSize() const
{
return fullscreenSize;
}
G3D::Vector2int16 getWindowSize() const
{
return windowSize;
}
FrameRateManagerMode getFrameRateManagerMode() const
{
return frameRateManagerMode;
}
AntialiasingMode getAntialiasingMode() const
{
return antialiasingMode;
}
QualityLevel getQualityLevel() const
{
return qualityLevel;
}
QualityLevel getEditQualityLevel() const
{
return editQualityLevel;
}
int getMaxQualityLevel()
{
return maxQualityLevel;
}
int getAutoQualityLevel() const
{
return autoQualityLevel;
}
ResolutionPreset getResolutionPreference() const
{
return resolutionPreference;
}
const RESOLUTIONENTRY& getResolutionPreset(ResolutionPreset preset) const;
// FRM would like to report latest setting. Subclass is free to ignore it
virtual void setAutoQualityLevel(int level) {}
float getMaxFrameRate()
{
float framerate[] = {30.f, 60.f, 75.f, 120.f, 144.f, 200.f, 240.f, 360.f, 9999999.f};
return framerate[Aya::GameBasicSettings::singleton().getMaxFramerate()];
}
float getMinFrameRate() const
{
return 30.0f;
}
bool getDrawConnectors() const
{
return drawConnectors;
}
void setDrawConnectors(bool value)
{
drawConnectors = value;
}
int getMinCullDistance() const
{
return minCullDistance;
}
bool getDebugShowBoundingBoxes() const
{
return debugShowBoundingBoxes;
}
bool getDebugReloadAssets() const
{
return debugReloadAssets;
}
bool getObjExportMergeByMaterial() const
{
return objExportMergeByMaterial;
}
bool getEnableFRM() const
{
return enableFRM;
}
bool getEagerBulkExecution() const
{
return eagerBulkExecution;
}
unsigned int getTextureCacheSize() const
{
return textureCacheSize;
}
unsigned int getMeshCacheSize() const
{
return meshCacheSize;
}
};
} // namespace Aya

View File

@@ -0,0 +1,32 @@
#include "Base/RenderStats.hpp"
#include "Utility/Profiling.hpp"
using namespace Aya;
RenderStats::RenderStats()
: cpuRenderTotal(new Aya::Profiling::CodeProfiler("3D CPU Total"))
, culling(new Aya::Profiling::CodeProfiler("Culling"))
, flip(new Aya::Profiling::CodeProfiler("Flipping Backbuffer"))
, renderObjects(new Aya::Profiling::CodeProfiler("Render Objects"))
, updateLighting(new Aya::Profiling::CodeProfiler("Update Lighting"))
, adorn2D(new Aya::Profiling::CodeProfiler("Adorn 2D"))
, adorn3D(new Aya::Profiling::CodeProfiler("Adorn 3D"))
, visualEngineSceneUpdater(new Aya::Profiling::CodeProfiler("Visual Engine Scene Updater"))
, finishRendering(new Aya::Profiling::CodeProfiler("Finish Rendering"))
, renderTargetUpdate(new Aya::Profiling::CodeProfiler("RenderTarget Update"))
, frameRateManager(new Aya::Profiling::CodeProfiler("Frame Rate Manager"))
, textureCompositor(new Aya::Profiling::CodeProfiler("Texture Compositor"))
, updateSceneGraph(new Aya::Profiling::CodeProfiler("Update SceneGraph"))
, updateAllInvalidParts(new Aya::Profiling::CodeProfiler("updateAllInvalidParts"))
, updateDynamicsAndAggregateStatics(new Aya::Profiling::CodeProfiler("updateDynamicsAndAggregateStatics"))
, updateDynamicParts(new Aya::Profiling::CodeProfiler("updateDynamicParts"))
{
}
RenderStats::~RenderStats() {}

View File

@@ -0,0 +1,103 @@
#pragma once
#include "boost/scoped_ptr.hpp"
#include "Utility/Profiling.hpp"
namespace Aya
{
namespace Profiling
{
class CodeProfiler;
}
struct RenderPassStats
{
unsigned int batches;
unsigned int faces;
unsigned int vertices;
unsigned int stateChanges;
unsigned int passChanges;
RenderPassStats()
: batches(0)
, faces(0)
, vertices(0)
, stateChanges(0)
, passChanges(0)
{
}
RenderPassStats& operator+=(const RenderPassStats& other)
{
batches += other.batches;
faces += other.faces;
vertices += other.vertices;
stateChanges += other.stateChanges;
passChanges += other.passChanges;
return *this;
}
RenderPassStats operator+(const RenderPassStats& other) const
{
RenderPassStats result = *this;
result += other;
return result;
}
};
struct ClusterStats
{
unsigned int clusters;
unsigned int parts;
ClusterStats()
: clusters(0)
, parts(0)
{
}
};
class RenderStats
{
public:
boost::scoped_ptr<Aya::Profiling::CodeProfiler> cpuRenderTotal;
boost::scoped_ptr<Aya::Profiling::CodeProfiler> culling;
boost::scoped_ptr<Aya::Profiling::CodeProfiler> flip;
boost::scoped_ptr<Aya::Profiling::CodeProfiler> renderObjects;
boost::scoped_ptr<Aya::Profiling::CodeProfiler> updateLighting;
boost::scoped_ptr<Aya::Profiling::CodeProfiler> adorn2D;
boost::scoped_ptr<Aya::Profiling::CodeProfiler> adorn3D;
boost::scoped_ptr<Aya::Profiling::CodeProfiler> visualEngineSceneUpdater;
boost::scoped_ptr<Aya::Profiling::CodeProfiler> finishRendering;
boost::scoped_ptr<Aya::Profiling::CodeProfiler> renderTargetUpdate;
boost::scoped_ptr<Aya::Profiling::CodeProfiler> frameRateManager;
boost::scoped_ptr<Aya::Profiling::CodeProfiler> textureCompositor;
boost::scoped_ptr<Aya::Profiling::CodeProfiler> updateSceneGraph;
boost::scoped_ptr<Aya::Profiling::CodeProfiler> updateAllInvalidParts;
boost::scoped_ptr<Aya::Profiling::CodeProfiler> updateDynamicsAndAggregateStatics;
boost::scoped_ptr<Aya::Profiling::CodeProfiler> updateDynamicParts;
RenderPassStats passTotal;
RenderPassStats passScene;
RenderPassStats passShadow;
RenderPassStats passUI;
RenderPassStats pass3DAdorns;
ClusterStats clusterFast;
ClusterStats clusterFastFW;
ClusterStats clusterFastHumanoid;
ClusterStats lastFrameFast;
unsigned lastFrameMegaClusterChunks;
RenderStats();
~RenderStats();
};
} // namespace Aya

View File

@@ -0,0 +1,30 @@
#pragma once
#include <boost/shared_ptr.hpp>
#include "boost/enable_shared_from_this.hpp"
#include "Vector2.hpp"
#include <string>
namespace Aya
{
typedef boost::shared_ptr<class TextureProxyBase> TextureProxyBaseRef;
class TextureProxyBase : public boost::enable_shared_from_this<TextureProxyBase>
{
private:
typedef boost::enable_shared_from_this<TextureProxyBase> Super;
public:
TextureProxyBase() {}
virtual ~TextureProxyBase() {}
virtual G3D::Vector2 getOriginalSize() = 0;
static const unsigned int numStrips = 32;
static float stripWidth()
{
return 1.0f / (float)numStrips;
}
};
} // namespace Aya

View File

@@ -0,0 +1,87 @@
#pragma once
namespace Aya
{
namespace Text
{
enum Font
{
FONT_LEGACY,
FONT_ARIAL,
FONT_ARIALBOLD,
FONT_SOURCESANS,
FONT_SOURCESANSBOLD,
FONT_SOURCESANSLIGHT,
FONT_SOURCESANSITALIC,
FONT_BODONI,
FONT_GARAMOND,
FONT_CARTOON,
FONT_CODE,
FONT_HIGHWAY,
FONT_SCIFI,
FONT_ARCADE,
FONT_FANTASY,
FONT_ANTIQUE,
FONT_SOURCESANSSEMIBOLD,
FONT_GOTHAM,
FONT_GOTHAMSEMIBOLD,
FONT_GOTHAMBOLD,
FONT_GOTHAMBLACK,
FONT_AMATICSC,
FONT_BANGERS,
FONT_CREEPSTER,
FONT_DENKONE,
FONT_FONDAMENTO,
FONT_FREDOKAONE,
FONT_GRENZEGOTISCH,
FONT_INDIEFLOWER,
FONT_JOSEFINSANS,
FONT_JURA,
FONT_KALAM,
FONT_LUCKIESTGUY,
FONT_MERRIWEATHER,
FONT_MICHROMA,
FONT_NUNITO,
FONT_OSWALD,
FONT_PATRICKHAND,
FONT_PERMANENTMARKER,
FONT_ROBOTO,
FONT_ROBOTOCONDENSED,
FONT_ROBOTOMONO,
FONT_SARPANCH,
FONT_SPECIALELITE,
FONT_TITILLIUMWEB,
FONT_UBUNTU,
FONT_NOTOSANS,
FONT_NOTOSANSMEDIUM,
FONT_NOTOSANSSEMIBOLD,
FONT_NOTOSANSBOLD,
FONT_NOTOSANSEXTRABOLD,
FONT_NOTOSANSBLACK,
FONT_NOTOSANSITALIC,
FONT_NOTOSANSLIGHT,
FONT_NOTOSANSEXTRALIGHT,
FONT_NOTOSANSTHIN,
FONT_CONSOLAS,
FONT_COMICSANS,
FONT_LAST
};
// Font drawing params - copied from G3D
enum XAlign
{
XALIGN_RIGHT,
XALIGN_LEFT,
XALIGN_CENTER
};
enum YAlign
{
YALIGN_TOP,
/*YALIGN_BASELINE,*/ YALIGN_CENTER,
YALIGN_BOTTOM
};
} // namespace Text
} // namespace Aya

View File

@@ -0,0 +1,68 @@
#pragma once
#include "Utility/G3DCore.hpp"
#include "Utility/Rotation2d.hpp"
#include "Base/Type.hpp"
#include <boost/shared_ptr.hpp>
namespace Aya
{
class Adorn;
namespace Graphics
{
class Texture;
class TextureManager;
class TextureAtlas;
}; // namespace Graphics
// abstract base class
class Typesetter
{
public:
virtual ~Typesetter() {}
virtual Vector2 draw(Adorn* adorn, const std::string& s, const Vector2& position, float size, bool autoScale, const Color4& color,
const Color4& outline, Aya::Text::XAlign xalign = Aya::Text::XALIGN_LEFT, Aya::Text::YAlign yalign = Aya::Text::YALIGN_TOP,
const Vector2& availableSpace = Vector2::zero(), const Rect2D& clippingRect = Rect2D::xyxy(-1, -1, -1, -1),
const Rotation2D& rotation = Rotation2D()) const = 0;
virtual int getCursorPositionInText(const std::string& s, const Aya::Vector2& pos2D, float size, Aya::Text::XAlign xalign,
Aya::Text::YAlign yalign, const Aya::Vector2& availableSpace, const Rotation2D& rotation, Aya::Vector2 cursorPos) const = 0;
/**
Useful for drawing centered text and boxes around text.
*/
virtual Vector2 measure(const std::string& s, float size, const Vector2& availableSpace = Vector2::zero(), bool* textFits = NULL) const = 0;
virtual void loadResources(Aya::Graphics::TextureManager* textureManager, Aya::Graphics::TextureAtlas* glyphAtlas) = 0;
virtual void releaseResources() = 0;
virtual const boost::shared_ptr<Graphics::Texture>& getTexture() const = 0;
static bool isCharNonWhitespace(char c)
{
return (c >= '!' && c <= '~');
}
static bool isCharWhitespace(char c)
{
return (c == ' ' || c == '\t' || c == '\n');
}
static bool isCharSupported(char c)
{
return isCharNonWhitespace(c) || isCharWhitespace(c);
}
static bool isStringSupported(std::string& stringToCheck)
{
for (std::string::iterator iter = stringToCheck.begin(); iter != stringToCheck.end(); ++iter)
{
if (!isCharSupported(*iter))
{
return false;
}
}
return true;
}
};
} // namespace Aya

View File

@@ -0,0 +1,79 @@
#include "Base/ViewBase.hpp"
#include "time.hpp"
#include "Debug.hpp"
#include "Utility/MachineIdUploader.hpp"
#include "boost/functional/hash.hpp"
namespace Aya
{
extern void RenderView_InitModule();
extern void RenderView_ShutdownModule();
static IViewBaseFactory** getFactory(CRenderSettings::GraphicsMode mode)
{
static IViewBaseFactory* s_rgFactories[6] = {0, 0, 0, 0, 0, 0};
if ((static_cast<size_t>(mode)) < ARRAYSIZE(s_rgFactories))
{
return s_rgFactories + static_cast<size_t>(mode);
}
else
{
return 0;
}
}
ViewBase* ViewBase::CreateView(CRenderSettings::GraphicsMode mode, OSContext* context, CRenderSettings* renderSettings)
{
IViewBaseFactory** ppfactory = getFactory(mode);
// did you call Aya::ViewBase::InitPluginModules?
AYAASSERT(ppfactory && *ppfactory);
if (ppfactory && *ppfactory)
{
return (*ppfactory)->Create(mode, context, renderSettings);
}
else
{
return 0;
}
}
void ViewBase::RegisterFactory(CRenderSettings::GraphicsMode mode, IViewBaseFactory* factory)
{
IViewBaseFactory** ppfactory = getFactory(mode);
AYAASSERT(ppfactory);
if (ppfactory)
{
*ppfactory = factory;
}
}
void ViewBase::render(IMetric* metric, double timeRenderJob)
{
if (timeRenderJob == 0.0)
timeRenderJob = Time::nowFastSec();
renderPrepare(metric);
renderPerform(timeRenderJob);
}
void ViewBase::InitPluginModules()
{
RenderView_InitModule();
}
void ViewBase::ShutdownPluginModules()
{
RenderView_ShutdownModule();
}
std::pair<unsigned, unsigned> ViewBase::setFrameDataCallback(const boost::function<void(void*)>& callback)
{
return std::make_pair(0, 0);
}
} // namespace Aya

View File

@@ -0,0 +1,122 @@
#pragma once
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include "Declarations.hpp"
#include "Base/RenderSettings.hpp"
namespace Aya
{
namespace Graphics
{
class VisualEngine;
}
class DataModel;
class ViewBase;
class FrameRateManager;
class CRenderSettings;
class RenderStats;
class AyaInterface IMetric;
class Instance;
enum ExporterFormat
{
ExporterFormat_Obj,
ExporterFormat_NumFormats
};
enum ExporterSaveType
{
ExporterSaveType_Everything,
ExporterSaveType_Selection,
ExporterSaveType_NumSaveTypes
};
struct OSContext
{
void* hWnd;
void* display;
int width;
int height;
// insert OS specific stuff here.
OSContext()
: hWnd(0)
, display(0)
, width(640)
, height(480)
{
}
};
class IViewBaseFactory
{
public:
virtual ViewBase* Create(CRenderSettings::GraphicsMode mode, OSContext* context, CRenderSettings* renderSettings) = 0;
};
class ViewBase
{
friend class Visit;
public:
static ViewBase* CreateView(CRenderSettings::GraphicsMode mode, OSContext* context, CRenderSettings* renderSettings);
static void RegisterFactory(CRenderSettings::GraphicsMode mode, IViewBaseFactory* factory);
// need this because we are statically linking.
static void InitPluginModules();
// it is bad form to need this. phase out please.
static void ShutdownPluginModules();
virtual void initResources() = 0;
virtual void bindWorkspace(boost::shared_ptr<Aya::DataModel> dataModel) = 0;
virtual void render(IMetric* metric, double timeJobStart);
virtual void renderPrepare(IMetric* metric) = 0;
virtual void renderPerform(double timeJobStart) = 0;
virtual void onResize(int cx, int cy) = 0;
virtual void buildGui(bool buildInGameGui = true) = 0;
virtual void renderThumb(unsigned char* data, int width, int height, bool crop, bool allowDolly) = 0;
virtual void garbageCollect() {}
virtual Instance* getWorkspace() = 0;
virtual RenderStats& getRenderStats() = 0;
virtual DataModel* getDataModel() = 0;
virtual Graphics::VisualEngine* getVisualEngine() = 0;
// use for pulling debug info only, please.
virtual FrameRateManager* getFrameRateManager()
{
return 0;
}
virtual double getMetricValue(const std::string& s)
{
return -1;
}
virtual bool exportScene(const std::string& filePath, ExporterSaveType saveType, ExporterFormat format) = 0;
virtual bool exportSceneThumbJSON(ExporterSaveType saveType, ExporterFormat format, bool encodeBase64, std::string& strOut) = 0;
virtual void queueAssetReload(const std::string& filePath) {};
virtual void immediateAssetReload(const std::string& filePath) = 0;
virtual void suspendView() = 0;
virtual void resumeView() = 0;
virtual std::pair<unsigned, unsigned> setFrameDataCallback(const boost::function<void(void*)>& callback);
virtual ~ViewBase() {}
};
} // namespace Aya

View File

@@ -0,0 +1,150 @@
#include "Base/ViewportBillboarder.hpp"
#include "DataModel/Filters.hpp"
#include "DataModel/Camera.hpp"
#include "World/World.hpp"
#include "World/ContactManager.hpp"
namespace Aya
{
ViewportBillboarder::ViewportBillboarder()
: guiScreenSize(NULL)
, alwaysOnTop(false)
, screenOffset2D(Vector2::zero())
{
}
ViewportBillboarder::ViewportBillboarder(const Vector3& partExtentRelativeOffset, const Vector3& partStudsOffset,
const Vector2& billboardSizeRelativeOffset, const UDim2& billboardSize, const Vector2* guiScreenSize)
: partExtentRelativeOffset(partExtentRelativeOffset)
, partStudsOffset(partStudsOffset)
, billboardSizeRelativeOffset(billboardSizeRelativeOffset)
, billboardSize(billboardSize)
, guiScreenSize(guiScreenSize)
, alwaysOnTop(false)
, screenOffset2D(Vector2::zero())
{
}
Vector2 ViewportBillboarder::getScreenOffset(const Rect2D& parentviewport, const Aya::Camera& camera, const CoordinateFrame& desiredModelView)
{
const CoordinateFrame& cameraFrame = camera.coordinateFrame();
Vector3 projectedTranslation = camera.project((cameraFrame * desiredModelView).translation);
return Math::roundVector2(projectedTranslation.xy());
}
void ViewportBillboarder::update(const Rect2D& parentviewport, const Camera& camera, Vector3 partSize, CoordinateFrame partCFrame)
{
Vector3 halfSize = partSize / 2;
Extents cameraSpaceExtents;
// calculate extents in camera space
for (int a = 0; a < 8; a++)
{
// go through all permutations (use bits of counter)
Vector3 extent((a & 4) ? halfSize.x : -halfSize.x, (a & 2) ? halfSize.y : -halfSize.y, (a & 1) ? halfSize.z : -halfSize.z);
Vector3 pextent = camera.coordinateFrame().pointToObjectSpace(partCFrame.pointToWorldSpace(extent));
cameraSpaceExtents.expandToContain(pextent);
}
Vector3 relativeOffsetInCameraSpace = (partExtentRelativeOffset + Vector3::one()) * 0.5f * cameraSpaceExtents.size() + cameraSpaceExtents.min();
Vector3 billboardCenterInCameraSpace = relativeOffsetInCameraSpace + partStudsOffset;
Vector3 billboardCenterInWorldSpace = camera.coordinateFrame().pointToWorldSpace(billboardCenterInCameraSpace);
Vector3 billboardCenterInProjSpace = camera.project(billboardCenterInWorldSpace);
if (billboardCenterInProjSpace.z > 0 && billboardCenterInProjSpace.z < 1000)
{
visibleAndValid = true;
}
else
{
visibleAndValid = false;
return;
}
float pixelsPerStud = billboardCenterInProjSpace.z;
Vector2 studsPerPixel(1.f / pixelsPerStud, 1.f / pixelsPerStud); // undo the perspective effect of finding extents in screenspace.
// -1 : to UI coordinates (0,0 upper left, )
Vector2 billboardSizeInStuds = (billboardSize * (Vector2::one() * pixelsPerStud)) * studsPerPixel;
if (guiScreenSize)
viewport = Rect2D(*guiScreenSize);
else
viewport = Rect2D(billboardSizeInStuds * pixelsPerStud);
Vector2 UIToCameraSpaceScaler(billboardSizeInStuds / viewport.wh());
billboardCenterInCameraSpace += Vector3(billboardSizeRelativeOffset * billboardSizeInStuds, 0);
CoordinateFrame desiredModelView;
desiredModelView.translation = billboardCenterInCameraSpace - Vector3(billboardSizeInStuds.x * 0.5f, billboardSizeInStuds.y * -0.5f, 0);
desiredModelView.rotation.set(UIToCameraSpaceScaler.x, 0, 0, 0, UIToCameraSpaceScaler.y, 0, 0, 0, 1);
if (alwaysOnTop)
screenOffset2D = getScreenOffset(parentviewport, camera, desiredModelView);
cframe = camera.coordinateFrame() * desiredModelView;
}
bool ViewportBillboarder::hitTest(
const Vector2int16& mousePosition, const Vector2int16& windowSize, Aya::Workspace* workspace, Vector2& billboardMousePosition)
{
const Aya::Camera& camera = *(workspace->getConstCamera());
Vector3 x0y0Screen, x1y1Screen;
if (alwaysOnTop)
{
x0y0Screen = Vector3(screenOffset2D, 0);
x1y1Screen = Vector3(screenOffset2D + viewport.wh(), 0);
}
else
{
Vector3 x0y0World = cframe.pointToWorldSpace(Vector3(viewport.x0(), -viewport.y0(), 0));
Vector3 x1y1World = cframe.pointToWorldSpace(Vector3(viewport.x1(), -viewport.y1(), 0));
x0y0Screen = camera.project(x0y0World);
x1y1Screen = camera.project(x1y1World);
}
Extents extents;
extents.expandToContain(Vector3(x0y0Screen.xy(), 0));
extents.expandToContain(Vector3(x1y1Screen.xy(), 0));
if (extents.contains(Vector3(mousePosition.x, mousePosition.y, 0)))
{
billboardMousePosition = Vector2((mousePosition.x - x0y0Screen.x) / (x1y1Screen.x - x0y0Screen.x) * viewport.width(),
(mousePosition.y - x0y0Screen.y) / (x1y1Screen.y - x0y0Screen.y) * viewport.height());
if (alwaysOnTop)
{
return true;
}
ContactManager& contactManager = *workspace->getWorld()->getContactManager();
RbxRay unitRay = camera.worldRay(mousePosition.x, mousePosition.y);
RbxRay searchRay = RbxRay::fromOriginAndDirection(unitRay.origin(), unitRay.direction() * 2048);
Vector3 partHitPointWorld;
FilterInvisibleNonColliding invisibleNonCollidingObjectsFilter; // invisible & non-colliding parts don't block mouse-clicks
if (contactManager.getHit(searchRay, NULL, &invisibleNonCollidingObjectsFilter, partHitPointWorld) == NULL)
{
// We didn't hit anything
return true;
}
Vector3 hitPointWorld = cframe.pointToWorldSpace(Vector3(billboardMousePosition.x, billboardMousePosition.y, 0));
Vector3 hitPointScreen = camera.project(hitPointWorld);
Vector3 partHitPointScreen = camera.project(partHitPointWorld);
if (partHitPointScreen.z < hitPointScreen.z)
{
return true;
}
return false;
}
return false;
}
} // namespace Aya

View File

@@ -0,0 +1,58 @@
#pragma once
#include "DataModel/Workspace.hpp"
#include "Utility/UDim.hpp"
namespace Aya
{
class ViewportBillboarder
{
private:
CoordinateFrame cframe;
Rect2D viewport;
bool visibleAndValid;
Vector2 screenOffset2D;
Vector2 getScreenOffset(const Rect2D& parentviewport, const Aya::Camera& camera, const CoordinateFrame& desiredModelView);
public:
Vector3 partExtentRelativeOffset;
Vector3 partStudsOffset;
Vector2 billboardSizeRelativeOffset;
UDim2 billboardSize;
const Vector2* guiScreenSize;
bool alwaysOnTop;
ViewportBillboarder();
ViewportBillboarder(const Vector3& partExtentRelativeOffset, const Vector3& partStudsOffset, const Vector2& billboardSizeRelativeOffset,
const UDim2& billboardSize, // studs* x + pixels
const Vector2* guiScreenSize // null for pixel-exact.
);
void update(const Rect2D& parentviewport, const Camera& camera, Vector3 partSize, CoordinateFrame partCFrame);
bool hitTest(const Vector2int16& mousePosition, const Vector2int16& windowSize, Aya::Workspace* workspace, Vector2& billboardMousePosition);
const Vector2& getScreenOffset() const
{
return screenOffset2D;
}
bool isVisibleAndValid() const
{
return visibleAndValid;
}
const Rect2D& getViewport() const
{
return viewport;
}
const CoordinateFrame& getCoordinateFrame() const
{
return cframe;
}
};
}; // namespace Aya

View File

@@ -0,0 +1,119 @@
#include "Core/Device.hpp"
#include "Utility/StandardOut.hpp"
LOGVARIABLE(Graphics, 0)
FASTFLAGVARIABLE(DebugGraphicsCrashOnLeaks, false) // d9mz - Oh well
FASTFLAGVARIABLE(GraphicsDebugMarkersEnable, false)
namespace Aya
{
namespace Graphics
{
DeviceContext::DeviceContext() {}
DeviceContext::~DeviceContext() {}
void DeviceContext::draw(Geometry* geometry, Geometry::Primitive primitive, unsigned int offset, unsigned int count, unsigned int indexRangeBegin,
unsigned int indexRangeEnd)
{
drawImpl(geometry, primitive, offset, count, indexRangeBegin, indexRangeEnd);
}
void DeviceContext::draw(const GeometryBatch& geometryBatch)
{
drawImpl(geometryBatch.getGeometry(), geometryBatch.getPrimitive(), geometryBatch.getOffset(), geometryBatch.getCount(),
geometryBatch.getIndexRangeBegin(), geometryBatch.getIndexRangeEnd());
}
void DeviceCaps::dumpToFLog(int channel) const
{
FASTLOG2(channel, "Caps: Shaders %d FFP %d", supportsShaders, supportsFFP);
FASTLOG5(channel, "Caps: Framebuffer %d (%d MRT, %d MSAA) Stencil %d 32bIdx %d", supportsFramebuffer, maxDrawBuffers, maxSamples, supportsStencil,
supportsIndex32);
FASTLOG4(channel, "Caps: Texture: DXT %d PVR %d ETC1 %d Half %d", supportsTextureDXT, supportsTexturePVR, supportsTextureETC1,
supportsTextureHalfFloat);
FASTLOG3(channel, "Caps: Texture: 3D %d NPOT %d PartialMips %d", supportsTexture3D, supportsTextureNPOT, supportsTexturePartialMipChain);
FASTLOG2(channel, "Caps: Texture: Size %d Units %d", maxTextureSize, maxTextureUnits);
FASTLOG3(channel, "Caps: ColorBGR %d HalfPixelOffset %d RTFlip %d", colorOrderBGR, needsHalfPixelOffset, requiresRenderTargetFlipping);
FASTLOG1(channel, "Caps: Retina %d", retina);
}
Device::Device()
: resourceListHead(NULL)
, resourceListTail(NULL)
{
}
Device::~Device()
{
if (resourceListHead)
{
FASTLOG(FLog::Graphics, "ERROR: Not all resources are destroyed!");
unsigned int index = 0;
for (Resource* cur = resourceListHead; cur; cur = cur->next, index++)
{
FASTLOG2(FLog::Graphics, "Leak %d: resource %p", index, cur);
FASTLOGS(FLog::Graphics, "Resource type %s", typeid(*cur).name());
Aya::StandardOut::singleton()->printf(MESSAGE_INFO, "Leak %d: resource %p", index, cur);
Aya::StandardOut::singleton()->printf(MESSAGE_INFO, "Resource type %s", typeid(*cur).name());
if (!cur->getDebugName().empty())
{
// FASTLOGS(FLog::Graphics, "Resource name %s", cur->getDebugName());
Aya::StandardOut::singleton()->printf(MESSAGE_INFO, "Resource name %s", cur->getDebugName().c_str());
}
}
if (FFlag::DebugGraphicsCrashOnLeaks)
AYACRASH();
else
AYAASSERT(false);
}
}
shared_ptr<Geometry> Device::createGeometry(const shared_ptr<VertexLayout>& layout, const shared_ptr<VertexBuffer>& vertexBuffer,
const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex)
{
std::vector<shared_ptr<VertexBuffer>> vertexBuffers;
vertexBuffers.push_back(vertexBuffer);
return createGeometryImpl(layout, vertexBuffers, indexBuffer, baseVertexIndex);
}
shared_ptr<Geometry> Device::createGeometry(const shared_ptr<VertexLayout>& layout, const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers,
const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex)
{
return createGeometryImpl(layout, vertexBuffers, indexBuffer, baseVertexIndex);
}
shared_ptr<Framebuffer> Device::createFramebuffer(const shared_ptr<Renderbuffer>& color, const shared_ptr<Renderbuffer>& depth)
{
std::vector<shared_ptr<Renderbuffer>> colors;
colors.push_back(color);
return createFramebufferImpl(colors, depth);
}
shared_ptr<Framebuffer> Device::createFramebuffer(const std::vector<shared_ptr<Renderbuffer>>& color, const shared_ptr<Renderbuffer>& depth)
{
return createFramebufferImpl(color, depth);
}
void Device::fireDeviceLost()
{
for (Resource* cur = resourceListTail; cur; cur = cur->prev)
cur->onDeviceLost();
}
void Device::fireDeviceRestored()
{
for (Resource* cur = resourceListHead; cur; cur = cur->next)
cur->onDeviceRestored();
}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,195 @@
#pragma once
#include "Core/Geometry.hpp"
#include "Core/Shader.hpp"
#include "Core/Texture.hpp"
#include "Core/Pix.hpp"
#include <string>
#include <boost/function.hpp>
namespace Aya
{
namespace Graphics
{
class RasterizerState;
class BlendState;
class DepthState;
class SamplerState;
class Framebuffer;
class DeviceContext
{
public:
enum BufferMask
{
Buffer_Color = 1 << 0,
Buffer_Depth = 1 << 1,
Buffer_Stencil = 1 << 2
};
virtual ~DeviceContext();
virtual void setDefaultAnisotropy(unsigned int value) = 0;
virtual void updateGlobalConstants(const void* data, size_t dataSize) = 0;
virtual void bindFramebuffer(Framebuffer* buffer) = 0;
virtual void clearFramebuffer(unsigned int mask, const float color[4], float depth, unsigned int stencil) = 0;
virtual void copyFramebuffer(Framebuffer* buffer, Texture* texture, int xOffset, int yOffset) = 0;
virtual void resolveFramebuffer(Framebuffer* msaaBuffer, Framebuffer* buffer, unsigned int mask) = 0;
virtual void discardFramebuffer(Framebuffer* buffer, unsigned int mask) = 0;
virtual void bindProgram(ShaderProgram* program) = 0;
virtual void setWorldTransforms4x3(const float* data, size_t matrixCount) = 0;
virtual void setConstant(int handle, const float* data, size_t vectorCount) = 0;
virtual void bindTexture(unsigned int stage, Texture* texture, const SamplerState& state) = 0;
virtual void setRasterizerState(const RasterizerState& state) = 0;
virtual void setBlendState(const BlendState& state) = 0;
virtual void setDepthState(const DepthState& state) = 0;
void draw(Geometry* geometry, Geometry::Primitive primitive, unsigned int offset, unsigned int count, unsigned int indexRangeBegin,
unsigned int indexRangeEnd);
void draw(const GeometryBatch& geometryBatch);
virtual void pushDebugMarkerGroup(const char* text) = 0;
virtual void popDebugMarkerGroup() = 0;
virtual void setDebugMarker(const char* text) = 0;
protected:
DeviceContext();
virtual void drawImpl(Geometry* geometry, Geometry::Primitive primitive, unsigned int offset, unsigned int count, unsigned int indexRangeBegin,
unsigned int indexRangeEnd) = 0;
};
struct DeviceCaps
{
bool supportsFramebuffer;
bool supportsShaders;
bool supportsFFP;
bool supportsStencil;
bool supportsIndex32;
bool supportsTextureDXT;
bool supportsTexturePVR;
bool supportsTextureHalfFloat;
bool supportsTexture3D;
bool supportsTextureNPOT;
bool supportsTextureETC1;
bool supportsTexturePartialMipChain;
unsigned int maxDrawBuffers;
unsigned int maxSamples;
unsigned int maxTextureSize;
unsigned int maxTextureUnits;
bool colorOrderBGR;
bool needsHalfPixelOffset;
bool requiresRenderTargetFlipping;
bool retina;
void dumpToFLog(int channel) const;
};
struct DeviceStats
{
float gpuFrameTime;
};
typedef boost::function<void(void*)> DeviceFrameDataCallback;
class Device
{
friend class Resource;
public:
enum API
{
API_BGFX,
API_OpenGL
};
static Device* create(API api, void* windowHandle, void* display, int w, int h);
virtual ~Device();
virtual bool validate() = 0;
virtual void resize(int w, int h) = 0;
virtual DeviceContext* beginFrame() = 0;
virtual void endFrame() = 0;
virtual Framebuffer* getMainFramebuffer() = 0;
virtual void defineGlobalConstants(size_t dataSize, const std::vector<ShaderGlobalConstant>& constants) = 0;
virtual std::string getAPIName() = 0;
virtual std::string getFeatureLevel() = 0;
virtual std::string getShadingLanguage() = 0;
virtual std::string createShaderSource(
const std::string& path, const std::string& defines, boost::function<std::string(const std::string&)> fileCallback) = 0;
virtual std::vector<char> createShaderBytecode(const std::string& source, const std::string& target, const std::string& entrypoint) = 0;
virtual shared_ptr<VertexShader> createVertexShader(const std::vector<char>& bytecode) = 0;
virtual shared_ptr<FragmentShader> createFragmentShader(const std::vector<char>& bytecode) = 0;
virtual shared_ptr<ShaderProgram> createShaderProgram(
const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader) = 0;
virtual shared_ptr<ShaderProgram> createShaderProgramFFP() = 0;
virtual shared_ptr<VertexBuffer> createVertexBuffer(size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage) = 0;
virtual shared_ptr<IndexBuffer> createIndexBuffer(size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage) = 0;
virtual shared_ptr<VertexLayout> createVertexLayout(const std::vector<VertexLayout::Element>& elements) = 0;
shared_ptr<Geometry> createGeometry(const shared_ptr<VertexLayout>& layout, const shared_ptr<VertexBuffer>& vertexBuffer,
const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex = 0);
shared_ptr<Geometry> createGeometry(const shared_ptr<VertexLayout>& layout, const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers,
const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex = 0);
virtual shared_ptr<Texture> createTexture(Texture::Type type, Texture::Format format, unsigned int width, unsigned int height, unsigned int depth,
unsigned int mipLevels, Texture::Usage usage) = 0;
virtual shared_ptr<Renderbuffer> createRenderbuffer(Texture::Format format, unsigned int width, unsigned int height, unsigned int samples) = 0;
shared_ptr<Framebuffer> createFramebuffer(
const shared_ptr<Renderbuffer>& color, const shared_ptr<Renderbuffer>& depth = shared_ptr<Renderbuffer>());
shared_ptr<Framebuffer> createFramebuffer(
const std::vector<shared_ptr<Renderbuffer>>& color, const shared_ptr<Renderbuffer>& depth = shared_ptr<Renderbuffer>());
virtual const DeviceCaps& getCaps() const = 0;
virtual DeviceStats getStatistics() const = 0;
virtual void suspend()
{
AYAASSERT(false); /* only DX11-Durango*/
}
virtual void resume()
{
AYAASSERT(false); /* only DX11-Durango*/
}
protected:
Resource* resourceListHead;
Resource* resourceListTail;
Device();
virtual shared_ptr<Geometry> createGeometryImpl(const shared_ptr<VertexLayout>& layout,
const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers, const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex) = 0;
virtual shared_ptr<Framebuffer> createFramebufferImpl(
const std::vector<shared_ptr<Renderbuffer>>& color, const shared_ptr<Renderbuffer>& depth) = 0;
void fireDeviceLost();
void fireDeviceRestored();
};
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,20 @@
#include "API/BGFX/DeviceBGFX.hpp"
#include "API/GL/DeviceGL.hpp"
namespace Aya
{
namespace Graphics
{
Device* Device::create(API api, void* windowHandle, void* display, int w, int h)
{
if (api == API_BGFX)
return new DeviceBGFX(windowHandle, display);
if (api == API_OpenGL)
return new DeviceGL(windowHandle, display, w, h);
throw Aya::runtime_error("Unsupported API: %d", api);
}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,36 @@
#include "Core/Framebuffer.hpp"
#include "Profiler.hpp"
namespace Aya
{
namespace Graphics
{
Renderbuffer::Renderbuffer(Device* device, Texture::Format format, unsigned int width, unsigned int height, unsigned int samples)
: Resource(device)
, format(format)
, width(width)
, height(height)
, samples(samples)
{
AYAPROFILER_COUNTER_ADD("memory/gpu/renderbuffer", Texture::getImageSize(format, width, height) * samples);
}
Renderbuffer::~Renderbuffer()
{
AYAPROFILER_COUNTER_SUB("memory/gpu/renderbuffer", Texture::getImageSize(format, width, height) * samples);
}
Framebuffer::Framebuffer(Device* device, unsigned int width, unsigned int height, unsigned int samples)
: Resource(device)
, width(width)
, height(height)
, samples(samples)
{
}
Framebuffer::~Framebuffer() {}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,72 @@
#pragma once
#include "Resource.hpp"
#include "Texture.hpp"
namespace Aya
{
namespace Graphics
{
class Renderbuffer : public Resource
{
public:
~Renderbuffer();
Texture::Format getFormat() const
{
return format;
}
unsigned int getWidth() const
{
return width;
}
unsigned int getHeight() const
{
return height;
}
unsigned int getSamples() const
{
return samples;
}
protected:
Renderbuffer(Device* device, Texture::Format format, unsigned int width, unsigned int height, unsigned int samples);
Texture::Format format;
unsigned int width;
unsigned int height;
unsigned int samples;
};
class Framebuffer : public Resource
{
public:
~Framebuffer();
virtual void download(void* data, unsigned int size) = 0;
unsigned int getWidth() const
{
return width;
}
unsigned int getHeight() const
{
return height;
}
unsigned int getSamples() const
{
return samples;
}
protected:
Framebuffer(Device* device, unsigned int width, unsigned int height, unsigned int samples);
unsigned int width;
unsigned int height;
unsigned int samples;
};
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,112 @@
#include "Core/Geometry.hpp"
#include "Profiler.hpp"
namespace Aya
{
namespace Graphics
{
VertexLayout::Element::Element(unsigned int stream, unsigned int offset, Format format, Semantic semantic, unsigned int semanticIndex)
: stream(stream)
, offset(offset)
, format(format)
, semantic(semantic)
, semanticIndex(semanticIndex)
{
}
VertexLayout::VertexLayout(Device* device, const std::vector<Element>& elements)
: Resource(device)
, elements(elements)
{
}
VertexLayout::~VertexLayout() {}
GeometryBuffer::GeometryBuffer(Device* device, size_t elementSize, size_t elementCount, Usage usage)
: Resource(device)
, elementSize(elementSize)
, elementCount(elementCount)
, usage(usage)
{
}
GeometryBuffer::~GeometryBuffer() {}
VertexBuffer::VertexBuffer(Device* device, size_t elementSize, size_t elementCount, Usage usage)
: GeometryBuffer(device, elementSize, elementCount, usage)
{
AYAPROFILER_COUNTER_ADD("memory/gpu/vertexbuffer", elementSize * elementCount);
}
VertexBuffer::~VertexBuffer()
{
AYAPROFILER_COUNTER_SUB("memory/gpu/vertexbuffer", elementSize * elementCount);
}
IndexBuffer::IndexBuffer(Device* device, size_t elementSize, size_t elementCount, Usage usage)
: GeometryBuffer(device, elementSize, elementCount, usage)
{
AYAPROFILER_COUNTER_ADD("memory/gpu/indexbuffer", elementSize * elementCount);
}
IndexBuffer::~IndexBuffer()
{
AYAPROFILER_COUNTER_SUB("memory/gpu/indexbuffer", elementSize * elementCount);
}
Geometry::Geometry(Device* device, const shared_ptr<VertexLayout>& layout, const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers,
const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex)
: Resource(device)
, layout(layout)
, vertexBuffers(vertexBuffers)
, indexBuffer(indexBuffer)
, baseVertexIndex(baseVertexIndex)
{
}
Geometry::~Geometry() {}
inline bool isCountValid(Geometry::Primitive primitive, unsigned int count)
{
switch (primitive)
{
case Geometry::Primitive_Triangles:
return count % 3 == 0;
case Geometry::Primitive_Lines:
return count % 2 == 0;
case Geometry::Primitive_Points:
return true;
case Geometry::Primitive_TriangleStrip:
return count != 1 && count != 2;
default:
return false;
}
}
GeometryBatch::GeometryBatch(const shared_ptr<Geometry>& geometry, Geometry::Primitive primitive, unsigned int count, unsigned int indexRangeSize)
: geometry(geometry)
, primitive(primitive)
, offset(0)
, count(count)
, indexRangeBegin(0)
, indexRangeEnd(indexRangeSize)
{
AYAASSERT(geometry && isCountValid(primitive, count));
}
GeometryBatch::GeometryBatch(const shared_ptr<Geometry>& geometry, Geometry::Primitive primitive, unsigned int offset, unsigned int count,
unsigned int indexRangeBegin, unsigned int indexRangeEnd)
: geometry(geometry)
, primitive(primitive)
, offset(offset)
, count(count)
, indexRangeBegin(indexRangeBegin)
, indexRangeEnd(indexRangeEnd)
{
AYAASSERT(geometry && isCountValid(primitive, count) && indexRangeBegin <= indexRangeEnd);
}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,201 @@
#pragma once
#include "Resource.hpp"
#include <vector>
namespace Aya
{
namespace Graphics
{
class VertexLayout : public Resource
{
public:
enum Semantic
{
Semantic_Position,
Semantic_Normal,
Semantic_Color,
Semantic_Texture,
Semantic_Count
};
enum Format
{
Format_Float1,
Format_Float2,
Format_Float3,
Format_Float4,
Format_Short2,
Format_Short4,
Format_UByte4,
Format_Color,
Format_Count
};
struct Element
{
unsigned int stream;
unsigned int offset;
Format format;
Semantic semantic;
unsigned int semanticIndex;
Element(unsigned int stream, unsigned int offset, Format format, Semantic semantic, unsigned int semanticIndex = 0);
};
~VertexLayout();
const std::vector<Element>& getElements() const
{
return elements;
}
protected:
VertexLayout(Device* device, const std::vector<Element>& elements);
std::vector<Element> elements;
};
class GeometryBuffer : public Resource
{
public:
enum Usage
{
Usage_Static,
Usage_Dynamic,
Usage_Count
};
enum LockMode
{
Lock_Normal,
Lock_Discard,
Lock_Count
};
~GeometryBuffer();
virtual void* lock(LockMode mode = Lock_Normal) = 0;
virtual void unlock() = 0;
virtual void upload(unsigned int offset, const void* data, unsigned int size) = 0;
size_t getElementSize() const
{
return elementSize;
}
size_t getElementCount() const
{
return elementCount;
}
protected:
GeometryBuffer(Device* device, size_t elementSize, size_t elementCount, Usage usage);
size_t elementSize;
size_t elementCount;
Usage usage;
};
class VertexBuffer : public GeometryBuffer
{
public:
~VertexBuffer();
protected:
VertexBuffer(Device* device, size_t elementSize, size_t elementCount, Usage usage);
};
class IndexBuffer : public GeometryBuffer
{
public:
~IndexBuffer();
protected:
IndexBuffer(Device* device, size_t elementSize, size_t elementCount, Usage usage);
};
class Geometry : public Resource
{
public:
enum Primitive
{
Primitive_Triangles,
Primitive_Lines,
Primitive_Points,
Primitive_TriangleStrip,
Primitive_Count
};
~Geometry();
std::vector<shared_ptr<VertexBuffer>> getVertexBuffers() { return vertexBuffers; }
shared_ptr<IndexBuffer> getIndexBuffer() { return indexBuffer; }
shared_ptr<VertexLayout> getVertexLayout() { return layout; }
protected:
Geometry(Device* device, const shared_ptr<VertexLayout>& layout, const std::vector<shared_ptr<VertexBuffer>>& vertexBuffers,
const shared_ptr<IndexBuffer>& indexBuffer, unsigned int baseVertexIndex);
shared_ptr<VertexLayout> layout;
std::vector<shared_ptr<VertexBuffer>> vertexBuffers;
shared_ptr<IndexBuffer> indexBuffer;
unsigned int baseVertexIndex;
};
class GeometryBatch
{
public:
GeometryBatch(const shared_ptr<Geometry>& geometry, Geometry::Primitive primitive, unsigned int count, unsigned int indexRangeSize);
GeometryBatch(const shared_ptr<Geometry>& geometry, Geometry::Primitive primitive, unsigned int offset, unsigned int count,
unsigned int indexRangeBegin, unsigned int indexRangeEnd);
Geometry* getGeometry() const
{
return geometry.get();
}
Geometry::Primitive getPrimitive() const
{
return primitive;
}
unsigned int getOffset() const
{
return offset;
}
unsigned int getCount() const
{
return count;
}
unsigned int getIndexRangeBegin() const
{
return indexRangeBegin;
}
unsigned int getIndexRangeEnd() const
{
return indexRangeEnd;
}
private:
shared_ptr<Geometry> geometry;
Geometry::Primitive primitive;
unsigned int offset;
unsigned int count;
unsigned int indexRangeBegin;
unsigned int indexRangeEnd;
};
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,66 @@
#include "Core/Pix.hpp"
#define _CRT_SECURE_NO_WARNINGS 1
#if PIX_ENABLED
#include "Core/Device.hpp"
#include <stdio.h>
#include <stdarg.h>
#include "FastLog.hpp"
FASTFLAG(GraphicsDebugMarkersEnable)
namespace Aya
{
namespace Graphics
{
void PixMarker(DeviceContext* ctx, const char* fmt, ...)
{
if (FFlag::GraphicsDebugMarkersEnable)
{
va_list va;
va_start(va, fmt);
char buffer[512];
vsnprintf(buffer, 512, fmt, va);
buffer[511] = 0;
ctx->setDebugMarker(buffer);
va_end(va);
}
}
PixScope::PixScope(DeviceContext* ctx, const char* fmt, ...)
{
devctx = ctx;
if (FFlag::GraphicsDebugMarkersEnable)
{
va_list va;
va_start(va, fmt);
char buffer[512];
vsnprintf(buffer, 512, fmt, va);
buffer[511] = 0;
ctx->pushDebugMarkerGroup(buffer);
va_end(va);
}
}
PixScope::~PixScope()
{
if (FFlag::GraphicsDebugMarkersEnable)
{
devctx->popDebugMarkerGroup();
}
}
} // namespace Graphics
} // namespace Aya
#else
static int dummy0001;
#endif

View File

@@ -0,0 +1,40 @@
#pragma once
#define PIX_ENABLED 1
#if PIX_ENABLED
#define PIX_CONCAT(a, b) PIX_CONCAT2(a, b)
#define PIX_CONCAT2(a, b) PIX_CONCAT3(a, b)
#define PIX_CONCAT3(a, b) a##b
#define PIX_MARKER(ctx, ...) Aya::Graphics::PixMarker(ctx, __VA_ARGS__)
#define PIX_SCOPE(ctx, ...) Aya::Graphics::PixScope PIX_CONCAT(pixScopeVar, __LINE__)(ctx, __VA_ARGS__)
// PIX support, D3D-only
// pix.cpp is in Core/D3D9
namespace Aya
{
namespace Graphics
{
class DeviceContext;
void PixMarker(DeviceContext* ctx, const char* fmt, ...);
struct PixScope
{
PixScope(DeviceContext* ctx, const char* fmt, ...);
~PixScope();
DeviceContext* devctx;
};
} // namespace Graphics
} // namespace Aya
#else
#define PIX_MARKER(...)
#define PIX_SCOPE(...)
#endif

View File

@@ -0,0 +1,61 @@
#include "Core/Resource.hpp"
#include "Core/Device.hpp"
namespace Aya
{
namespace Graphics
{
Resource::Resource(Device* device)
: device(device)
, prev(NULL)
, next(NULL)
{
if (device->resourceListHead)
{
AYAASSERT(device->resourceListTail);
prev = device->resourceListTail;
device->resourceListTail->next = this;
device->resourceListTail = this;
}
else
{
AYAASSERT(!device->resourceListTail);
device->resourceListHead = this;
device->resourceListTail = this;
}
}
Resource::~Resource()
{
if (prev)
prev->next = next;
else
{
AYAASSERT(device->resourceListHead == this);
device->resourceListHead = next;
}
if (next)
next->prev = prev;
else
{
AYAASSERT(device->resourceListTail == this);
device->resourceListTail = prev;
}
}
void Resource::onDeviceLost() {}
void Resource::onDeviceRestored() {}
void Resource::setDebugName(const std::string& value)
{
debugName = value;
}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,40 @@
#pragma once
#include "boost.hpp"
#include "Debug.hpp"
namespace Aya
{
namespace Graphics
{
class Device;
class Resource : boost::noncopyable
{
friend class Device;
public:
explicit Resource(Device* device);
virtual ~Resource();
virtual void onDeviceLost();
virtual void onDeviceRestored();
const std::string& getDebugName() const
{
return debugName;
}
void setDebugName(const std::string& value);
protected:
Device* device;
Resource* prev;
Resource* next;
std::string debugName;
};
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,55 @@
#include "Core/Shader.hpp"
#include <boost/algorithm/string.hpp>
LOGGROUP(Graphics)
namespace Aya
{
namespace Graphics
{
VertexShader::VertexShader(Device* device)
: Resource(device)
{
}
VertexShader::~VertexShader() {}
FragmentShader::FragmentShader(Device* device)
: Resource(device)
{
}
FragmentShader::~FragmentShader() {}
ShaderProgram::ShaderProgram(Device* device, const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader)
: Resource(device)
, vertexShader(vertexShader)
, fragmentShader(fragmentShader)
{
}
ShaderProgram::~ShaderProgram() {}
void ShaderProgram::dumpToFLog(const std::string& text, int channel)
{
std::vector<std::string> messages;
boost::split(messages, text, boost::is_from_range('\n', '\n'));
while (!messages.empty() && messages.back().empty())
messages.pop_back();
for (size_t i = 0; i < messages.size(); ++i)
FASTLOGS(channel, "%s", messages[i]);
}
ShaderGlobalConstant::ShaderGlobalConstant(const char* name, unsigned int offset, unsigned int size)
: name(name)
, offset(offset)
, size(size)
{
}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,64 @@
#pragma once
#include "Resource.hpp"
#include <vector>
#include <string>
namespace Aya
{
namespace Graphics
{
class VertexShader : public Resource
{
public:
~VertexShader();
virtual void reloadBytecode(const std::vector<char>& bytecode) = 0;
protected:
VertexShader(Device* device);
};
class FragmentShader : public Resource
{
public:
~FragmentShader();
virtual void reloadBytecode(const std::vector<char>& bytecode) = 0;
protected:
FragmentShader(Device* device);
};
class ShaderProgram : public Resource
{
public:
~ShaderProgram();
virtual int getConstantHandle(const char* name) const = 0;
virtual unsigned int getMaxWorldTransforms() const = 0;
virtual unsigned int getSamplerMask() const = 0;
static void dumpToFLog(const std::string& text, int channel);
protected:
ShaderProgram(Device* device, const shared_ptr<VertexShader>& vertexShader, const shared_ptr<FragmentShader>& fragmentShader);
shared_ptr<VertexShader> vertexShader;
shared_ptr<FragmentShader> fragmentShader;
};
struct ShaderGlobalConstant
{
const char* name;
unsigned int offset;
unsigned int size;
ShaderGlobalConstant(const char* name, unsigned int offset, unsigned int size);
};
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,9 @@
#include "Core/States.hpp"
namespace Aya
{
namespace Graphics
{
}
} // namespace Aya

View File

@@ -0,0 +1,339 @@
#pragma once
#include <boost/functional/hash_fwd.hpp>
namespace Aya
{
namespace Graphics
{
template<typename T>
struct StateHasher
{
std::size_t operator()(const T& state) const
{
return state.getHashId();
}
};
class RasterizerState
{
public:
enum CullMode
{
Cull_None,
Cull_Back,
Cull_Front,
Cull_Count
};
RasterizerState(CullMode cullMode, int depthBias = 0)
: cullMode(cullMode)
, depthBias(depthBias)
{
}
CullMode getCullMode() const
{
return cullMode;
}
int getDepthBias() const
{
return depthBias;
}
bool operator==(const RasterizerState& other) const
{
return cullMode == other.cullMode && depthBias == other.depthBias;
}
bool operator!=(const RasterizerState& other) const
{
return !(*this == other);
}
size_t getHashId() const
{
size_t seed = 0;
boost::hash_combine(seed, cullMode);
boost::hash_combine(seed, depthBias);
return seed;
}
private:
CullMode cullMode;
int depthBias;
};
class BlendState
{
public:
enum Mode
{
Mode_None,
Mode_Additive,
Mode_Multiplicative,
Mode_AlphaBlend,
Mode_PremultipliedAlphaBlend,
Mode_Count
};
enum Factor
{
Factor_One,
Factor_Zero,
Factor_DstColor,
Factor_SrcAlpha,
Factor_InvSrcAlpha,
Factor_DstAlpha,
Factor_InvDstAlpha,
Factor_Count
};
enum ColorMask
{
Color_None = 0,
Color_R = 1 << 0,
Color_G = 1 << 1,
Color_B = 1 << 2,
Color_A = 1 << 3,
Color_All = Color_R | Color_G | Color_B | Color_A
};
BlendState(Mode mode, unsigned int colorMask = Color_All)
{
struct Translation
{
Factor src;
Factor dst;
};
static const Translation translation[Mode_Count] = {
{Factor_One, Factor_Zero},
{Factor_One, Factor_One},
{Factor_DstColor, Factor_Zero},
{Factor_SrcAlpha, Factor_InvSrcAlpha},
{Factor_One, Factor_InvSrcAlpha},
};
construct(translation[mode].src, translation[mode].dst, translation[mode].src, translation[mode].dst, colorMask);
}
BlendState(Factor colorSrcIn, Factor colorDstIn, Factor alphaSrcIn, Factor alphaDstIn, unsigned int colorMask = Color_All)
{
construct(colorSrcIn, colorDstIn, alphaSrcIn, alphaDstIn, colorMask);
}
BlendState(Factor src, Factor dst, unsigned int colorMask = Color_All)
{
construct(src, dst, src, dst, colorMask);
}
bool blendingNeeded() const
{
return colorSrc != Factor_One || colorDst != Factor_Zero || alphaSrc != Factor_One || alphaDst != Factor_Zero;
}
bool separateAlphaBlend() const
{
return colorSrc != alphaSrc || colorDst != alphaDst;
}
unsigned int getColorMask() const
{
return colorMask;
}
unsigned int getColorSrc() const
{
return colorSrc;
}
unsigned int getColorDst() const
{
return colorDst;
}
unsigned int getAlphaSrc() const
{
return alphaSrc;
}
unsigned int getAlphaDst() const
{
return alphaDst;
}
bool operator==(const BlendState& other) const
{
return colorSrc == other.colorSrc && colorDst == other.colorDst && alphaSrc == other.alphaSrc && alphaDst == other.alphaDst &&
colorMask == other.colorMask;
}
bool operator!=(const BlendState& other) const
{
return !(*this == other);
}
size_t getHashId() const
{
size_t seed = 0;
boost::hash_combine(seed, colorSrc);
boost::hash_combine(seed, colorDst);
boost::hash_combine(seed, alphaSrc);
boost::hash_combine(seed, alphaDst);
boost::hash_combine(seed, colorMask);
return seed;
}
private:
void construct(Factor colorSrcIn, Factor colorDstIn, Factor alphaSrcIn, Factor alphaDstIn, unsigned int colorMask)
{
this->colorSrc = colorSrcIn;
this->colorDst = colorDstIn;
this->alphaSrc = alphaSrcIn;
this->alphaDst = alphaDstIn;
this->colorMask = colorMask;
}
unsigned int colorMask;
Factor colorSrc;
Factor colorDst;
Factor alphaSrc;
Factor alphaDst;
};
class DepthState
{
public:
enum Function
{
Function_Always,
Function_Less,
Function_LessEqual,
Function_None,
Function_Count
};
enum StencilMode
{
Stencil_None,
Stencil_IsNotZero,
Stencil_UpdateZFail,
Stencil_Increment,
Stencil_Decrement,
Stencil_IsNotZeroReplace,
Stencil_Count
};
DepthState(Function function, bool write, StencilMode stencilMode = Stencil_None)
: function(function)
, write(write)
, stencilMode(stencilMode)
{
}
Function getFunction() const
{
return function;
}
bool getWrite() const
{
return write;
}
StencilMode getStencilMode() const
{
return stencilMode;
}
bool operator==(const DepthState& other) const
{
return function == other.function && write == other.write && stencilMode == other.stencilMode;
}
bool operator!=(const DepthState& other) const
{
return !(*this == other);
}
size_t getHashId() const
{
size_t seed = 0;
boost::hash_combine(seed, function);
boost::hash_combine(seed, write);
boost::hash_combine(seed, stencilMode);
return seed;
}
private:
Function function;
bool write;
StencilMode stencilMode;
};
class SamplerState
{
public:
enum Filter
{
Filter_Point,
Filter_Linear,
Filter_Anisotropic,
Filter_Count
};
enum Address
{
Address_Wrap,
Address_Clamp,
Address_Count
};
SamplerState(Filter filter, Address address = Address_Wrap, unsigned int anisotropy = 0)
: filter(filter)
, address(address)
, anisotropy(anisotropy)
{
}
Filter getFilter() const
{
return filter;
}
Address getAddress() const
{
return address;
}
unsigned int getAnisotropy() const
{
return anisotropy;
}
bool operator==(const SamplerState& other) const
{
return filter == other.filter && address == other.address && anisotropy == other.anisotropy;
}
bool operator!=(const SamplerState& other) const
{
return !(*this == other);
}
size_t getHashId() const
{
size_t seed = 0;
boost::hash_combine(seed, filter);
boost::hash_combine(seed, address);
boost::hash_combine(seed, anisotropy);
return seed;
}
private:
Filter filter;
Address address;
unsigned int anisotropy;
};
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,172 @@
#include "Core/Texture.hpp"
#include "Profiler.hpp"
FASTFLAGVARIABLE(GraphicsTextureCommitChanges, false)
namespace Aya
{
namespace Graphics
{
struct FormatDescription
{
unsigned char bpp;
bool compressed;
bool depth;
};
static const FormatDescription gTextureFormats[Texture::Format_Count] = {
{8, false, false},
{16, false, false},
{16, false, false},
{32, false, false},
{32, false, false},
{64, false, false},
{4, true, false},
{8, true, false},
{8, true, false},
{2, true, false},
{2, true, false},
{4, true, false},
{4, true, false},
{4, true, false},
{16, false, true},
{32, false, true},
};
TextureRegion::TextureRegion()
: x(0)
, y(0)
, z(0)
, width(0)
, height(0)
, depth(0)
{
}
TextureRegion::TextureRegion(unsigned int x, unsigned int y, unsigned int z, unsigned int width, unsigned int height, unsigned int depth)
: x(x)
, y(y)
, z(z)
, width(width)
, height(height)
, depth(depth)
{
}
TextureRegion::TextureRegion(unsigned int x, unsigned int y, unsigned int width, unsigned int height)
: x(x)
, y(y)
, z(0)
, width(width)
, height(height)
, depth(1)
{
}
Texture::Texture(
Device* device, Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, Usage usage)
: Resource(device)
, type(type)
, format(format)
, width(width)
, height(height)
, depth(depth)
, mipLevels(mipLevels)
, usage(usage)
{
AYAASSERT(width > 0 && height > 0 && depth > 0);
AYAASSERT(mipLevels > 0 && mipLevels <= getMaxMipCount(width, height, depth));
AYAASSERT(type == Type_3D || depth == 1);
if (usage != Usage_Renderbuffer)
{
AYAPROFILER_COUNTER_ADD("memory/gpu/texture", getTextureSize(type, format, width, height, depth, mipLevels));
}
}
Texture::~Texture()
{
if (usage != Usage_Renderbuffer)
{
AYAPROFILER_COUNTER_SUB("memory/gpu/texture", getTextureSize(type, format, width, height, depth, mipLevels));
}
}
bool Texture::isFormatCompressed(Format format)
{
return gTextureFormats[format].compressed;
}
bool Texture::isFormatDepth(Format format)
{
return gTextureFormats[format].depth;
}
unsigned int Texture::getFormatBits(Format format)
{
return gTextureFormats[format].bpp;
}
unsigned int Texture::getImageSize(Format format, unsigned int width, unsigned int height)
{
const FormatDescription& desc = gTextureFormats[format];
switch (format)
{
case Format_BC1:
case Format_BC2:
case Format_BC3:
case Format_ETC1:
return ((width + 3) / 4) * ((height + 3) / 4) * (desc.bpp * 16 / 8);
case Format_PVRTC_RGB2:
case Format_PVRTC_RGBA2:
return (std::max(width, 16u) * std::max(height, 8u) * 2 + 7) / 8;
case Format_PVRTC_RGB4:
case Format_PVRTC_RGBA4:
return (std::max(width, 8u) * std::max(height, 8u) * 4 + 7) / 8;
default:
AYAASSERT(!desc.compressed);
return width * height * (desc.bpp / 8);
}
}
unsigned int Texture::getTextureSize(Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels)
{
unsigned int result = 0;
for (unsigned int mip = 0; mip < mipLevels; ++mip)
result += Texture::getImageSize(format, Texture::getMipSide(width, mip), Texture::getMipSide(height, mip)) * Texture::getMipSide(depth, mip);
return result * (type == Texture::Type_Cube ? 6 : 1);
}
unsigned int Texture::getMipSide(unsigned int value, unsigned int mip)
{
unsigned int result = value >> mip;
return result ? result : 1;
}
unsigned int Texture::getMaxMipCount(unsigned int width, unsigned int height, unsigned int depth)
{
unsigned int result = 0;
while (width || height || depth)
{
result++;
width /= 2;
height /= 2;
depth /= 2;
}
return result;
}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,149 @@
#pragma once
#include "Resource.hpp"
namespace Aya
{
namespace Graphics
{
class Renderbuffer;
struct TextureRegion
{
unsigned int x;
unsigned int y;
unsigned int z;
unsigned int width;
unsigned int height;
unsigned int depth;
TextureRegion();
TextureRegion(unsigned int x, unsigned int y, unsigned int z, unsigned int width, unsigned int height, unsigned int depth);
TextureRegion(unsigned int x, unsigned int y, unsigned int width, unsigned int height);
};
class Texture : public Resource
{
public:
enum Type
{
Type_2D,
Type_2DMultisampled,
Type_3D,
Type_Cube,
Type_Count
};
enum Usage
{
Usage_Static,
Usage_Dynamic,
Usage_Renderbuffer,
Usage_Count
};
enum Format
{
Format_L8,
Format_LA8,
Format_RGB5A1,
Format_RGBA8,
Format_RG16,
Format_RGBA16F,
Format_BC1,
Format_BC2,
Format_BC3,
Format_PVRTC_RGB2,
Format_PVRTC_RGBA2,
Format_PVRTC_RGB4,
Format_PVRTC_RGBA4,
Format_ETC1,
Format_D16,
Format_D24S8,
Format_Count
};
struct LockResult
{
void* data;
unsigned int rowPitch;
unsigned int slicePitch;
};
~Texture();
virtual void upload(unsigned int index, unsigned int mip, const TextureRegion& region, const void* data, unsigned int size) = 0;
virtual bool download(unsigned int index, unsigned int mip, void* data, unsigned int size) = 0;
virtual bool supportsLocking() const = 0;
virtual LockResult lock(unsigned int index, unsigned int mip, const TextureRegion& region) = 0;
virtual void unlock(unsigned int index, unsigned int mip) = 0;
virtual shared_ptr<Renderbuffer> getRenderbuffer(unsigned int index, unsigned int mip) = 0;
virtual void commitChanges() = 0;
virtual void generateMipmaps() = 0;
Type getType() const
{
return type;
}
Format getFormat() const
{
return format;
}
unsigned int getWidth() const
{
return width;
}
unsigned int getHeight() const
{
return height;
}
unsigned int getDepth() const
{
return depth;
}
unsigned int getMipLevels() const
{
return mipLevels;
}
Usage getUsage() const
{
return usage;
}
static bool isFormatCompressed(Format format);
static bool isFormatDepth(Format format);
static unsigned int getFormatBits(Format format);
static unsigned int getImageSize(Format format, unsigned int width, unsigned int height);
static unsigned int getTextureSize(Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels);
static unsigned int getMipSide(unsigned int value, unsigned int mip);
static unsigned int getMaxMipCount(unsigned int width, unsigned int height, unsigned int depth);
protected:
Texture(
Device* device, Type type, Format format, unsigned int width, unsigned int height, unsigned int depth, unsigned int mipLevels, Usage usage);
Type type;
Format format;
unsigned int width;
unsigned int height;
unsigned int depth;
unsigned int mipLevels;
Usage usage;
};
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,869 @@
#include "Render/AdornRender.hpp"
#include "DataModel/DataModel.hpp"
#include "DataModel/Workspace.hpp"
#include "Render/VisualEngine.hpp"
#include "Render/VertexStreamer.hpp"
#include "Base/Typesetter.hpp"
#include "Render/TextureManager.hpp"
#include "Render/ShaderManager.hpp"
#include "Render/TextureRef.hpp"
#include "Base/MeshGen.hpp"
#include "Base/FrameRateManager.hpp"
#include "Base/RenderStats.hpp"
#include "Core/Texture.hpp"
#include "Core/Device.hpp"
#include "Core/States.hpp"
#include "Core/Shader.hpp"
#include "Utility/IndexBox.hpp"
#include "Utility/Rotation2d.hpp"
#include "Profiler.hpp"
#ifdef _WIN32
#define alloca _alloca
#endif
namespace Aya
{
namespace Graphics
{
struct AdornVertex
{
Vector3 position;
Vector2 uv;
Vector3 normal;
AdornVertex() {}
AdornVertex(const Vector3& position, const Vector3& normal)
: position(position)
, normal(normal)
{
}
};
static GeometryBatch* createBatch(
Device* device, const shared_ptr<VertexLayout>& layout, const std::vector<AdornVertex>& vertices, const std::vector<unsigned short>& indices)
{
shared_ptr<VertexBuffer> vbuf = device->createVertexBuffer(sizeof(AdornVertex), vertices.size(), GeometryBuffer::Usage_Static);
vbuf->upload(0, &vertices[0], vertices.size() * sizeof(AdornVertex));
shared_ptr<IndexBuffer> ibuf = device->createIndexBuffer(sizeof(unsigned short), indices.size(), GeometryBuffer::Usage_Static);
ibuf->upload(0, &indices[0], indices.size() * sizeof(unsigned short));
return new GeometryBatch(device->createGeometry(layout, vbuf, ibuf), Geometry::Primitive_Triangles, indices.size(), vertices.size());
}
static GeometryBatch* createBox(Device* device, const shared_ptr<VertexLayout>& layout)
{
IndexBox box(-Vector3::one(), Vector3::one());
std::vector<AdornVertex> vertices;
std::vector<unsigned short> indices;
for (int face = 0; face < 6; ++face)
{
Vector3 n = box.getFaceNormal(face);
Vector3 v0, v1, v2, v3;
box.getFaceCorners(face, v0, v1, v2, v3);
vertices.push_back(AdornVertex(v0, n));
vertices.push_back(AdornVertex(v1, n));
vertices.push_back(AdornVertex(v2, n));
vertices.push_back(AdornVertex(v3, n));
indices.push_back(face * 4 + 0);
indices.push_back(face * 4 + 1);
indices.push_back(face * 4 + 2);
indices.push_back(face * 4 + 0);
indices.push_back(face * 4 + 2);
indices.push_back(face * 4 + 3);
}
return createBatch(device, layout, vertices, indices);
}
static GeometryBatch* createCylinderX(Device* device, const shared_ptr<VertexLayout>& layout, int sides, bool zeroNormalBottom)
{
std::vector<AdornVertex> vertices;
std::vector<unsigned short> indices;
RotationAngle increment(360.f / sides);
RotationAngle current;
// center vertices for caps
vertices.push_back(AdornVertex(Vector3(-1, 0, 0), zeroNormalBottom ? Vector3::zero() : Vector3(-1, 0, 0)));
vertices.push_back(AdornVertex(Vector3(+1, 0, 0), zeroNormalBottom ? Vector3::zero() : Vector3(+1, 0, 0)));
size_t vertexOffset = vertices.size();
size_t verticesPerSide = 4;
for (int side = 0; side < sides; ++side)
{
Vector3 vcur(0, current.getSin(), current.getCos());
vertices.push_back(AdornVertex(vcur - Vector3(1, 0, 0), vcur));
vertices.push_back(AdornVertex(vcur + Vector3(1, 0, 0), vcur));
vertices.push_back(AdornVertex(vcur - Vector3(1, 0, 0), zeroNormalBottom ? vcur : Vector3(-1, 0, 0)));
vertices.push_back(AdornVertex(vcur + Vector3(1, 0, 0), zeroNormalBottom ? vcur : Vector3(+1, 0, 0)));
current = current.combine(increment);
}
for (int side = 0; side < sides; ++side)
{
int side0 = side;
int side1 = (side + 1) % sides;
// side quad
indices.push_back((unsigned short)(vertexOffset + side0 * verticesPerSide + 0));
indices.push_back((unsigned short)(vertexOffset + side0 * verticesPerSide + 1));
indices.push_back((unsigned short)(vertexOffset + side1 * verticesPerSide + 1));
indices.push_back((unsigned short)(vertexOffset + side0 * verticesPerSide + 0));
indices.push_back((unsigned short)(vertexOffset + side1 * verticesPerSide + 1));
indices.push_back((unsigned short)(vertexOffset + side1 * verticesPerSide + 0));
// caps
indices.push_back(0);
indices.push_back((unsigned short)(vertexOffset + side0 * verticesPerSide + 2));
indices.push_back((unsigned short)(vertexOffset + side1 * verticesPerSide + 2));
indices.push_back(1);
indices.push_back((unsigned short)(vertexOffset + side1 * verticesPerSide + 3));
indices.push_back((unsigned short)(vertexOffset + side0 * verticesPerSide + 3));
}
return createBatch(device, layout, vertices, indices);
}
static GeometryBatch* createSphere(Device* device, const shared_ptr<VertexLayout>& layout)
{
const int sidesU = 18;
const int sidesV = 9;
std::vector<AdornVertex> vertices;
std::vector<unsigned short> indices;
RotationAngle incrementU(360.f / sidesU);
RotationAngle incrementV(180.f / sidesV);
Vector3 avg;
for (int i = 0; i < 4; ++i)
{
RotationAngle u = (i & 1) ? incrementU : RotationAngle();
RotationAngle v = (i & 2) ? incrementV : RotationAngle();
Vector3 pos(v.getSin() * u.getCos(), v.getSin() * u.getSin(), v.getCos());
avg += pos;
}
float radius = 1 / (avg / 4).length();
RotationAngle currentU;
for (int u = 0; u < sidesU; ++u)
{
RotationAngle currentV;
for (int v = 0; v <= sidesV; ++v)
{
Vector3 pos(currentV.getSin() * currentU.getCos(), currentV.getSin() * currentU.getSin(), currentV.getCos());
vertices.push_back(AdornVertex(pos * radius, pos));
currentV = currentV.combine(incrementV);
}
currentU = currentU.combine(incrementU);
}
for (int u = 0; u < sidesU; ++u)
{
int u0 = u;
int u1 = (u + 1) % sidesU;
for (int v = 0; v < sidesV; ++v)
{
int v0 = v;
int v1 = v + 1;
indices.push_back(u0 * (sidesV + 1) + v0);
indices.push_back(u1 * (sidesV + 1) + v1);
indices.push_back(u1 * (sidesV + 1) + v0);
indices.push_back(u0 * (sidesV + 1) + v0);
indices.push_back(u0 * (sidesV + 1) + v1);
indices.push_back(u1 * (sidesV + 1) + v1);
}
}
return createBatch(device, layout, vertices, indices);
}
static GeometryBatch* createCone(Device* device, const shared_ptr<VertexLayout>& layout)
{
const int sides = 12;
std::vector<AdornVertex> vertices;
std::vector<unsigned short> indices;
RotationAngle increment(360.f / sides);
RotationAngle current;
vertices.push_back(AdornVertex(Vector3(0, 0, 0), Vector3(-1, 0, 0)));
for (int side = 0; side < sides; ++side)
{
RotationAngle next = (side + 1 < sides) ? current.combine(increment) : RotationAngle();
Vector3 vcur(0, current.getSin(), current.getCos());
Vector3 vnext(0, next.getSin(), next.getCos());
Vector3 apex(1, 0, 0);
size_t vertexOffset = vertices.size();
vertices.push_back(AdornVertex(vcur, vcur));
vertices.push_back(AdornVertex(vnext, vnext));
vertices.push_back(AdornVertex(apex, (vcur + vnext).unit()));
vertices.push_back(AdornVertex(vcur, Vector3(-1, 0, 0)));
vertices.push_back(AdornVertex(vnext, Vector3(-1, 0, 0)));
// side
indices.push_back((unsigned short)(vertexOffset + 0));
indices.push_back((unsigned short)(vertexOffset + 2));
indices.push_back((unsigned short)(vertexOffset + 1));
// base
indices.push_back(0);
indices.push_back((unsigned short)(vertexOffset + 3));
indices.push_back((unsigned short)(vertexOffset + 4));
current = next;
}
return createBatch(device, layout, vertices, indices);
}
static GeometryBatch* createTorus(Device* device, const shared_ptr<VertexLayout>& layout)
{
const int sides = 12;
std::vector<AdornVertex> vertices;
std::vector<unsigned short> indices;
RotationAngle increment(360.f / sides);
RotationAngle current;
vertices.push_back(AdornVertex(Vector3(0, 0, 0), Vector3(-1, 0, 0)));
for (int side = 0; side < sides; ++side)
{
RotationAngle next = (side + 1 < sides) ? current.combine(increment) : RotationAngle();
Vector3 vcur(0, current.getSin(), current.getCos());
Vector3 vnext(0, next.getSin(), next.getCos());
Vector3 apex(1, 0, 0);
size_t vertexOffset = vertices.size();
vertices.push_back(AdornVertex(vcur, vcur));
vertices.push_back(AdornVertex(vnext, vnext));
vertices.push_back(AdornVertex(apex, (vcur + vnext).unit()));
vertices.push_back(AdornVertex(vcur, Vector3(-1, 0, 0)));
vertices.push_back(AdornVertex(vnext, Vector3(-1, 0, 0)));
// side
indices.push_back((unsigned short)(vertexOffset + 0));
indices.push_back((unsigned short)(vertexOffset + 2));
indices.push_back((unsigned short)(vertexOffset + 1));
// base
indices.push_back(0);
indices.push_back((unsigned short)(vertexOffset + 3));
indices.push_back((unsigned short)(vertexOffset + 4));
current = next;
}
return createBatch(device, layout, vertices, indices);
}
AdornRender::AdornMaterial::AdornMaterial()
: colorHandle(-1)
, pixelInfoHandle(-1)
{
}
AdornRender::AdornMaterial::AdornMaterial(const shared_ptr<ShaderProgram>& program)
: program(program)
, colorHandle(program ? program->getConstantHandle("Color") : -1)
, pixelInfoHandle(program ? program->getConstantHandle("PixelInfo") : -1)
{
}
AdornRender::AdornRender(VisualEngine* visualEngine, const DataModel* dataModel)
: visualEngine(visualEngine)
, dataModel(dataModel)
, currentTextureType(BatchTextureType_Color)
{
std::vector<VertexLayout::Element> elements;
elements.push_back(VertexLayout::Element(0, offsetof(AdornVertex, position), VertexLayout::Format_Float3, VertexLayout::Semantic_Position));
elements.push_back(VertexLayout::Element(0, offsetof(AdornVertex, uv), VertexLayout::Format_Float2, VertexLayout::Semantic_Texture));
elements.push_back(VertexLayout::Element(0, offsetof(AdornVertex, normal), VertexLayout::Format_Float3, VertexLayout::Semantic_Normal));
shared_ptr<VertexLayout> layout = visualEngine->getDevice()->createVertexLayout(elements);
batchBox.reset(createBox(visualEngine->getDevice(), layout));
batchCylinderX.reset(createCylinderX(visualEngine->getDevice(), layout, 12, false));
batchSphere.reset(createSphere(visualEngine->getDevice(), layout));
batchCone.reset(createCone(visualEngine->getDevice(), layout));
batchAALineCylinderX.reset(createCylinderX(visualEngine->getDevice(), layout, 12, true));
materials[Material_Default] = visualEngine->getShaderManager()->getProgramOrFFP("AdornLightingVS", "AdornFS");
materials[Material_NoLighting] = visualEngine->getShaderManager()->getProgramOrFFP("AdornVS", "AdornFS");
materials[Material_SelfLit] = visualEngine->getShaderManager()->getProgramOrFFP("AdornSelfLitVS", "AdornFS");
materials[Material_SelfLitHighlight] = visualEngine->getShaderManager()->getProgramOrFFP("AdornSelfLitHighlightVS", "AdornFS");
materials[Material_AALine] = visualEngine->getShaderManager()->getProgramOrFFP("AdornAALineVS", "AdornAALineFS");
materials[Material_Outline] = visualEngine->getShaderManager()->getProgramOrFFP("AdornOutlineVS", "AdornOutlineFS");
}
Aya::Rect2D AdornRender::getViewport() const
{
return Rect2D::xywh(0, 0, visualEngine->getViewWidth(), visualEngine->getViewHeight());
}
const Camera* AdornRender::getCamera() const
{
return dataModel->getWorkspace()->getConstCamera();
}
void AdornRender::setTexture(int id, const Aya::TextureProxyBaseRef& t)
{
if (t)
{
currentTexture = boost::polymorphic_downcast<TextureProxy*>(t.get())->getTexture();
}
else
{
currentTexture.reset();
}
}
Rect2D AdornRender::getTextureSize(const Aya::TextureProxyBaseRef& texture) const
{
return Aya::Rect2D(texture->getOriginalSize());
}
void AdornRender::rect2dImpl(
const Vector2& x0y0, const Vector2& x1y0, const Vector2& x0y1, const Vector2& x1y1, const Vector2& tex0, const Vector2& tex1, const Color4& color)
{
Vector2 px0y0(x0y0);
Vector2 px1y0(x1y0);
Vector2 px0y1(x0y1);
Vector2 px1y1(x1y1);
float height = currentHeight;
px0y0.y = height - px0y0.y;
px1y0.y = height - px1y0.y;
px0y1.y = height - px0y1.y;
px1y1.y = height - px1y1.y;
visualEngine->getVertexStreamer()->rectBlt(currentTexture, color, px0y0, px1y0, px0y1, px1y1, tex0, tex1, currentTextureType, getIgnoreTexture());
}
void AdornRender::line2d(const Aya::Vector2& p0, const Aya::Vector2& p1, const Aya::Color4& color)
{
visualEngine->getVertexStreamer()->line(p0.x, currentHeight - p0.y, p1.x, currentHeight - p1.y, &color[0]);
}
Vector2 AdornRender::drawFont2DImpl(Adorn* target, const std::string& s, const Vector2& position, float size, bool autoScale, const Color4& color,
const Color4& outline, Text::Font font, Text::XAlign xalign, Text::YAlign yalign, const Vector2& availableSpace, const Rect2D& clippingRect,
const Rotation2D& rotation)
{
const shared_ptr<Typesetter>& typesetter = visualEngine->getTypesetter(font);
if (!typesetter)
return Vector2::zero();
const shared_ptr<Texture>& tex = typesetter->getTexture();
shared_ptr<Texture> oldTexture = tex;
oldTexture.swap(currentTexture);
currentTextureType = BatchTextureType_Font;
Vector2 result = typesetter->draw(target, s, position, size, autoScale, color, outline, xalign, yalign, availableSpace, clippingRect, rotation);
currentTextureType = BatchTextureType_Color;
oldTexture.swap(currentTexture);
return result;
}
Vector2 AdornRender::get2DStringBounds(const std::string& s, float size, Text::Font font, const Vector2& availableSpace) const
{
const shared_ptr<Typesetter>& typesetter = visualEngine->getTypesetter(font);
if (!typesetter)
return Vector2::zero();
return typesetter->measure(s, size, availableSpace, NULL);
}
TextureProxyBaseRef AdornRender::createTextureProxy(const ContentId& id, bool& waiting, bool bBlocking, const std::string& context)
{
TextureRef texture = visualEngine->getTextureManager()->load(id, TextureManager::Fallback_None, context);
waiting = (texture.getStatus() == TextureRef::Status_Waiting);
if (texture.getStatus() == TextureRef::Status_Loaded)
{
return TextureProxyBaseRef(new TextureProxy(texture));
}
else
{
return TextureProxyBaseRef();
}
}
TextureProxyBaseRef AdornRender::createTextureProxy(int width, int height)
{
return TextureProxyBaseRef(new TextureProxy(
TextureRef(visualEngine->getDevice()->createTexture(Texture::Type_2D, Texture::Format_RGBA8, width, height, 1, 1, Texture::Usage_Static))));
}
Aya::signal<void()>& AdornRender::getUnbindResourcesSignal()
{
return unbindResourcesSignal;
}
void AdornRender::setObjectToWorldMatrix(const CoordinateFrame& c)
{
currentCFrame = c;
}
static const float kSqrt3 = 1.7320508f;
void AdornRender::box(const AABox& box, const Color4& solidColor)
{
Vector3 center = currentCFrame.pointToWorldSpace(box.center());
Vector3 extent = box.extent();
submitMesh(*batchBox, Material_NoLighting, center, currentCFrame.rotation, extent * 0.5f, solidColor, Sphere(center, extent.max() / 2 * kSqrt3));
}
void AdornRender::box(const CoordinateFrame& cFrame, const Vector3& size, const Color4& color, int zIndex, bool alwaysOnTop)
{
submitMesh(*batchBox, Material_NoLighting, cFrame.translation, cFrame.rotation, size, color, Sphere(cFrame.translation, size.max() / 2 * kSqrt3),
0.0f, zIndex, alwaysOnTop);
}
void AdornRender::cylinder(
const CoordinateFrame& cFrame, const float radius, const float height, const Color4& color, const int zIndex, const bool alwaysOnTop)
{
submitMesh(*batchCylinderX, Material_NoLighting, cFrame.translation, cFrame.rotation, Vector3(height / 2, radius, radius), color,
Sphere(cFrame.translation, std::max(radius, height / 2) * kSqrt3), 0.0f, zIndex, alwaysOnTop);
}
void AdornRender::cylinderAlongX(float radius, float length, const Color4& solidColor, bool cap)
{
Vector3 center = currentCFrame.translation;
submitMesh(*batchCylinderX, getMaterial(), center, currentCFrame.rotation, Vector3(length / 2, radius, radius), solidColor,
Sphere(center, std::max(radius, length / 2) * kSqrt3));
}
void AdornRender::sphere(const Sphere& sphere, const Color4& solidColor)
{
Vector3 center = currentCFrame.pointToWorldSpace(sphere.center);
float radius = sphere.radius;
submitMesh(*batchSphere, getMaterial(), center, currentCFrame.rotation, Vector3(radius, radius, radius), solidColor, Sphere(center, radius));
}
void AdornRender::sphere(const CoordinateFrame& cFrame, float radius, const Color4& color, int zIndex, bool alwaysOnTop)
{
submitMesh(*batchSphere, Material_NoLighting, cFrame.translation, cFrame.rotation, Vector3(radius, radius, radius), color,
Sphere(cFrame.translation, radius), 0.0f, zIndex, alwaysOnTop);
}
void AdornRender::extrusion(I3DLinearFunc* trajectory, int trajectorysegments, I3DLinearFunc* profile, int profilesegments, const Color4& color,
bool closeTrajectory, bool closeProfile)
{
float radius = profile->eval(0).length();
for (int i = 0; i < trajectorysegments; ++i)
{
Vector3 from = currentCFrame.pointToWorldSpace(trajectory->eval(static_cast<float>(i) / trajectorysegments));
Vector3 to = currentCFrame.pointToWorldSpace(
trajectory->eval((i + 1 == trajectorysegments && closeTrajectory) ? 0.f : static_cast<float>(i + 1) / trajectorysegments));
Vector3 axisX = (to - from).unit();
Vector3 axisY = (G3D::abs(axisX.y) < 0.7f ? Vector3(0, 1, 0) : Vector3(1, 0, 0)).unitCross(axisX);
Vector3 axisZ = axisX.unitCross(axisY);
Matrix3 rotation;
rotation.setColumn(0, axisX);
rotation.setColumn(1, axisY);
rotation.setColumn(2, axisZ);
Vector3 center = (from + to) / 2;
float length = (to - from).length();
submitMesh(*batchCylinderX, getMaterial(), center, rotation, Vector3(length / 2, radius, radius), color, Sphere(center, length / 2));
}
}
void AdornRender::axes(const Color4& xColor, const Color4& yColor, const Color4& zColor, float scale) {}
void AdornRender::cone(const CoordinateFrame& cFrame, float radius, float height, const Color4& color, int zIndex, bool alwaysOnTop)
{
submitMesh(*batchCone, Material_NoLighting, cFrame.translation, cFrame.rotation, Vector3(height, radius, radius), color,
Sphere(cFrame.translation, std::max(radius, height) * kSqrt3), 0.0f, zIndex, alwaysOnTop);
}
void AdornRender::ray(const RbxRay& ray, const Color4& color)
{
float length = ray.direction().length();
Sphere worldBounds(currentCFrame.pointToWorldSpace(ray.origin() + ray.direction() / 2), length / 2);
const float axisStalkLength = 1.f;
const float axisHeadLength = 0.3f;
const float axisLength = axisStalkLength + axisHeadLength;
const float axisHeadBaseRadius = 0.075f;
const float axisStalkRadius = axisHeadBaseRadius / 3;
float scale = length / axisLength;
Vector3 stalkCenter = currentCFrame.pointToWorldSpace(ray.origin() + ray.direction() / 2 * (axisStalkLength / axisLength));
float stalkScaleX = axisStalkLength / 2 * scale;
float stalkScaleYZ = axisStalkRadius * scale;
Vector3 coneOrigin = currentCFrame.pointToWorldSpace(ray.origin() + ray.direction() * (axisStalkLength / axisLength));
float coneScaleX = axisHeadLength * scale;
float coneScaleYZ = axisHeadBaseRadius * scale;
Vector3 axisX = currentCFrame.vectorToWorldSpace(ray.direction().unit());
Vector3 axisY = (G3D::abs(axisX.y) < 0.7f ? Vector3(0, 1, 0) : Vector3(1, 0, 0)).unitCross(axisX);
Vector3 axisZ = axisX.unitCross(axisY);
Matrix3 rotation;
rotation.setColumn(0, axisX);
rotation.setColumn(1, axisY);
rotation.setColumn(2, axisZ);
submitMesh(*batchCylinderX, getMaterial(), stalkCenter, rotation, Vector3(stalkScaleX, stalkScaleYZ, stalkScaleYZ), color, worldBounds);
submitMesh(*batchCone, getMaterial(), coneOrigin, rotation, Vector3(coneScaleX, coneScaleYZ, coneScaleYZ), color, worldBounds);
}
void AdornRender::line3d(const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color)
{
const Vector3 p0 = currentCFrame.vectorToWorldSpace(startPoint) + currentCFrame.translation;
const Vector3 p1 = currentCFrame.vectorToWorldSpace(endPoint) + currentCFrame.translation;
visualEngine->getVertexStreamer()->line3d(p0.x, p0.y, p0.z, p1.x, p1.y, p1.z, &color[0]);
}
void AdornRender::line3dAA(
const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color, float thickness, int zIndex, bool alwaysOnTop)
{
// these points cant be behind the camera position, lets project them on infinite plane defined by cam near plane
const RenderCamera camera = visualEngine->getCamera();
Vector3 camHeading = camera.getViewMatrix().upper3x3().row(2);
Plane pl = Plane(camHeading, camera.getPosition() + camHeading * -0.5f);
float distanceStart = pl.distance(startPoint);
float distanceEnd = pl.distance(endPoint);
if (distanceStart > 0 && distanceEnd > 0)
return; // nothing to do here
Vector3 startP = startPoint;
if (distanceStart > 0)
{
Vector3 dir = (endPoint - startPoint).direction();
RbxRay ray = RbxRay(startPoint, dir);
startP = ray.intersectionPlane(pl);
}
Vector3 endP = endPoint;
if (distanceEnd > 0)
{
Vector3 dir = (startPoint - endPoint).direction();
RbxRay ray = RbxRay(endPoint, dir);
endP = ray.intersectionPlane(pl);
}
Vector3 startToEnd = endP - startP;
Vector3 center = startP + startToEnd * 0.5f;
Vector3 scale = Vector3(startToEnd.length() * 0.5f, 1, 1);
Matrix3 rotMat = Matrix3::identity();
float lngth = fabs(startToEnd.direction().unit().dot(Vector3(1, 0, 0)));
if (lngth < 1)
{
Vector3 rotAxis = startToEnd.direction().cross(Vector3(1, 0, 0));
float rotAngle = -acos(startToEnd.direction().dot(Vector3(1, 0, 0)));
rotMat = Matrix3::fromAxisAngle(rotAxis, rotAngle);
}
submitMesh(*batchAALineCylinderX, Material_AALine, center, rotMat, scale, color, Sphere(center, scale.max() / 2 * kSqrt3), thickness, zIndex,
alwaysOnTop);
}
void AdornRender::quad(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Vector3& v3, const Color4& color, const Vector2& v0tex,
const Vector2& v2tex, int zIndex, bool alwaysOnTop)
{
if ((v2 - v0).isZero())
return;
visualEngine->getVertexStreamer()->spriteBlt3D(currentTexture, &color[0], currentTextureType, currentCFrame.pointToWorldSpace(v0),
currentCFrame.pointToWorldSpace(v1), currentCFrame.pointToWorldSpace(v2), currentCFrame.pointToWorldSpace(v3), v0tex, v2tex, zIndex,
alwaysOnTop && zIndex >= 0);
}
void AdornRender::convexPolygon2d(const Vector2* v, int countv, const Color4& color)
{
// swap from UI convention (0,0 top left), to GFX convention (0,0 bottom left)
Vector2* vertices = (Vector2*)alloca(sizeof(Vector2) * countv);
for (int i = 0; i < countv; ++i)
{
vertices[i] = Vector2(v[i].x, currentHeight - v[i].y);
}
short* indices = (short*)alloca(sizeof(short) * (countv - 2) * 3);
short start = 0;
int icount = 0;
for (int i = 1; i < countv - 1; ++i)
{
indices[icount++] = start;
indices[icount++] = i;
indices[icount++] = i + 1;
}
AYAASSERT(icount == (countv - 2) * 3);
AYAASSERT(icount <= 0xFFFF);
visualEngine->getVertexStreamer()->triangleList2d(color, vertices, countv, indices, icount);
}
void AdornRender::convexPolygon(const Vector3* v, int countv, const Color4& color)
{
short* indices = (short*)alloca(sizeof(short) * (countv - 2) * 3);
short start = 0;
int icount = 0;
for (int i = 1; i < countv - 1; ++i)
{
indices[icount++] = start;
indices[icount++] = i;
indices[icount++] = i + 1;
}
AYAASSERT(icount == (countv - 2) * 3);
AYAASSERT(icount <= 0xFFFF);
visualEngine->getVertexStreamer()->triangleList(color, currentCFrame, v, countv, indices, icount);
}
bool AdornRender::isVisible(const Extents& extents, const CoordinateFrame& cframe)
{
return visualEngine->getCameraCullFrm().isVisible(extents, cframe);
}
AdornRender::~AdornRender()
{
unbindResourcesSignal();
}
void AdornRender::explosion(const Sphere& sphere) {}
void AdornRender::preSubmitPass()
{
currentHeight = visualEngine->getViewHeight();
}
void AdornRender::postSubmitPass()
{
currentTexture.reset();
}
struct AdornMeshMaterialComparator
{
bool operator()(const AdornMesh& lhs, const AdornMesh& rhs) const
{
return (lhs.material != rhs.material) ? lhs.material < rhs.material : memcmp(&lhs.color, &rhs.color, sizeof(lhs.color)) < 0;
}
};
struct AdornMeshDistanceComparator
{
bool operator()(const AdornMesh& lhs, const AdornMesh& rhs) const
{
return lhs.distanceKey > rhs.distanceKey;
}
};
void AdornRender::prepareRenderPass()
{
std::sort(meshesOpaque.begin(), meshesOpaque.end(), AdornMeshMaterialComparator());
std::sort(meshesTransparent.begin(), meshesTransparent.end(), AdornMeshDistanceComparator());
for (unsigned i = 0; i < Adorn::maximumZIndex + 1; ++i)
std::sort(meshesNoDepthTest[i].begin(), meshesNoDepthTest[i].end(), AdornMeshDistanceComparator());
}
void AdornRender::finishRenderPass()
{
meshesOpaque.clear();
meshesTransparent.clear();
for (unsigned i = 0; i < Adorn::maximumZIndex + 1; ++i)
meshesNoDepthTest[i].clear();
visualEngine->getVertexStreamer()->cleanUpFrameData();
}
void AdornRender::render(DeviceContext* context, RenderPassStats& stats)
{
AYAPROFILER_SCOPE("Render", "Adorns");
AYAPROFILER_SCOPE("GPU", "Adorns");
shared_ptr<Texture> defaultTexture = visualEngine->getTextureManager()->getFallbackTexture(TextureManager::Fallback_White);
AYAASSERT(defaultTexture);
PIX_SCOPE(context, "AR::Adorns");
context->bindTexture(0, defaultTexture.get(), SamplerState::Filter_Linear);
context->setDepthState(DepthState(DepthState::Function_LessEqual, true));
context->setRasterizerState(RasterizerState::Cull_Back);
context->setBlendState(BlendState::Mode_None);
renderMeshes(context, meshesOpaque, stats);
context->setBlendState(BlendState::Mode_AlphaBlend);
renderMeshes(context, meshesTransparent, stats);
}
void AdornRender::renderNoDepth(DeviceContext* context, RenderPassStats& stats, int renderIndex)
{
shared_ptr<Texture> defaultTexture = visualEngine->getTextureManager()->getFallbackTexture(TextureManager::Fallback_White);
AYAASSERT(defaultTexture);
PIX_SCOPE(context, "AR::Adorns");
context->bindTexture(0, defaultTexture.get(), SamplerState::Filter_Linear);
context->setRasterizerState(RasterizerState::Cull_Back);
context->setBlendState(BlendState::Mode_AlphaBlend);
renderMeshes(context, meshesNoDepthTest[renderIndex], stats);
}
void AdornRender::submitMesh(const GeometryBatch& batch, Material material, const Vector3& translation, const Matrix3& rotation, const Vector3& scale,
const Color4& color, const Sphere& worldBounds, float thickness, int zIndex, bool alwaysOnTop)
{
const RenderCamera& camera = visualEngine->getCamera();
// Skip meshes if we don't know how to render them
if (materials[material].colorHandle < 0)
return;
// Visibility culling
if (!camera.isVisible(worldBounds))
return;
// FRM distance culling
float sqDistance = (visualEngine->getCamera().getPosition() - worldBounds.center).squaredLength();
FrameRateManager* frm = visualEngine->getFrameRateManager();
if (sqDistance > frm->GetRenderCullSqDistance())
return;
frm->AddBlockQuota(/* blockCount= */ 5, sqDistance, /* isInSpatialHash= */ false);
CoordinateFrame cframe(rotation * Matrix3::fromDiagonal(scale), translation);
AdornMesh mesh = {material, &batch, 0, thickness, zIndex, alwaysOnTop, color, cframe};
if (zIndex >= 0 && zIndex <= Adorn::maximumZIndex)
{
mesh.distanceKey = (visualEngine->getCamera().getPosition() - translation).squaredLength();
meshesNoDepthTest[zIndex].push_back(mesh);
}
else if (color.a < 1 || material == Material_AALine)
{
mesh.distanceKey = (visualEngine->getCamera().getPosition() - translation).squaredLength();
meshesTransparent.push_back(mesh);
}
else
{
meshesOpaque.push_back(mesh);
}
}
void AdornRender::renderMeshes(DeviceContext* context, const std::vector<AdornMesh>& meshes, RenderPassStats& stats)
{
Material currentMaterial = Material_Default;
Color4 currentColor;
for (size_t i = 0; i < meshes.size(); ++i)
{
const AdornMesh& mesh = meshes[i];
if (mesh.zIndex >= 0)
context->setDepthState(DepthState(mesh.alwaysOnTop ? DepthState::Function_Always : DepthState::Function_LessEqual, false));
if (i == 0 || currentMaterial != mesh.material)
{
currentMaterial = mesh.material;
currentColor = mesh.color;
context->bindProgram(materials[currentMaterial].program.get());
context->setConstant(materials[currentMaterial].colorHandle, &currentColor.r, 1);
stats.passChanges++;
}
else if (currentColor != mesh.color)
{
currentColor = mesh.color;
context->setConstant(materials[currentMaterial].colorHandle, &currentColor.r, 1);
}
if (mesh.lineThickness > 0)
{
Vector2 screenSize = Vector2(visualEngine->getViewWidth(), visualEngine->getViewHeight());
float pixelScale = tanf(dataModel->getWorkspace()->getCamera()->getFieldOfView() * 0.5f) / screenSize.y;
Vector4 pixelInfo = Vector4(pixelScale, screenSize.x, screenSize.y / screenSize.x, mesh.lineThickness);
context->setConstant(materials[currentMaterial].pixelInfoHandle, &pixelInfo.x, 1);
}
Matrix4 transform(mesh.cframe);
context->setWorldTransforms4x3(transform[0], 1);
context->draw(*mesh.batch);
stats.batches++;
stats.faces += mesh.batch->getCount() / 3;
stats.vertices += mesh.batch->getIndexRangeEnd() - mesh.batch->getIndexRangeBegin();
}
}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,178 @@
#pragma once
#include "Base/Adorn.hpp"
#include "Base/TextureProxyBase.hpp"
#include "Render/Image.hpp"
#include "Render/TextureRef.hpp"
#include "Utility/G3DCore.hpp"
#include "VertexStreamer.hpp"
namespace Aya
{
class DataModel;
struct RenderPassStats;
} // namespace Aya
namespace Aya
{
namespace Graphics
{
class VisualEngine;
class Texture;
class ShaderProgram;
class GeometryBatch;
class DeviceContext;
struct AdornMesh
{
Adorn::Material material;
const GeometryBatch* batch;
float distanceKey;
float lineThickness; // this is only AA-line specific, but not worth creating special mesh type for it
int zIndex;
bool alwaysOnTop;
Color4 color;
CoordinateFrame cframe;
};
class TextureProxy : public Aya::TextureProxyBase
{
public:
TextureProxy(const TextureRef& texture)
: texture(texture)
{
}
const shared_ptr<Texture>& getTexture() const
{
return texture.getTexture();
}
virtual G3D::Vector2 getOriginalSize()
{
const ImageInfo& info = texture.getInfo();
return G3D::Vector2(info.width, info.height);
}
private:
TextureRef texture;
};
class AdornRender : public Adorn
{
public:
AdornRender(VisualEngine* visualEngine, const DataModel* dataModel);
virtual ~AdornRender();
virtual void prepareRenderPass();
virtual void finishRenderPass();
void preSubmitPass();
void postSubmitPass();
void render(DeviceContext* context, RenderPassStats& stats);
void renderNoDepth(DeviceContext* context, RenderPassStats& stats, int renderIndex);
virtual Aya::Rect2D getViewport() const;
virtual const Camera* getCamera() const;
TextureProxyBaseRef createTextureProxy(const ContentId& id, bool& waiting, bool bBlocking = false, const std::string& context = "");
TextureProxyBaseRef createTextureProxy(int width, int height);
virtual void setTexture(int id, const Aya::TextureProxyBaseRef& t);
virtual Rect2D getTextureSize(const TextureProxyBaseRef& texture) const;
virtual void rect2dImpl(const Vector2& x0y0, const Vector2& x1y0, const Vector2& x0y1, const Vector2& x1y1, const Vector2& tex0,
const Vector2& tex1, const Color4& color);
virtual void line2d(const Aya::Vector2& p0, const Aya::Vector2& p1, const Aya::Color4& color);
virtual Vector2 get2DStringBounds(const std::string& s, float size, Text::Font font, const Vector2& availableSpace) const;
virtual Vector2 drawFont2DImpl(Adorn* target, const std::string& s, const Vector2& position, float size, bool autoScale, const Color4& color,
const Color4& outline, Text::Font font, Text::XAlign xalign, Text::YAlign yalign, const Vector2& availableSpace, const Rect2D& clippingRect,
const Rotation2D& rotation);
virtual void setObjectToWorldMatrix(const CoordinateFrame& c);
virtual void box(const AABox& box, const Color4& solidColor);
virtual void box(const CoordinateFrame& cFrame, const Vector3& size, const Color4& color, int zIndex, bool alwaysOnTop);
virtual void sphere(const Sphere& sphere, const Color4& solidColor);
virtual void sphere(const CoordinateFrame& cFrame, float radius, const Color4& color, int zIndex, bool alwaysOnTop);
virtual void cylinderAlongX(float radius, float length, const Color4& solidColor, bool cap);
virtual void cylinder(const CoordinateFrame& cFrame, float radius, float height, const Color4& color, int zIndex, bool alwaysOnTop);
virtual void cone(const CoordinateFrame& cFrame, float radius, float height, const Color4& color, int zIndex, bool alwaysOnTop);
virtual void ray(const RbxRay& ray, const Color4& color);
virtual void axes(const Color4& xColor, const Color4& yColor, const Color4& zColor, float scale);
virtual void extrusion(I3DLinearFunc* trajectory, int trajectorysegments, I3DLinearFunc* profile, int profilesegments, const Color4& color,
bool closeTrajectory, bool closeProfile);
virtual void explosion(const Sphere& sphere);
virtual void line3d(const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color);
virtual void line3dAA(
const Vector3& startPoint, const Vector3& endPoint, const Aya::Color4& color, float thickness, int zIndex, bool alwaysOnTop);
virtual void quad(const Vector3& v0, const Vector3& v1, const Vector3& v2, const Vector3& v3, const Color4& color, const Vector2& v0tex,
const Vector2& v2tex, int zIndex, bool alwaysOnTop);
virtual void convexPolygon2d(const Vector2* v, int countv, const Color4& color);
virtual void convexPolygon(const Vector3* v, int countv, const Color4& color);
virtual bool isVisible(const Extents& extents, const CoordinateFrame& cframe);
private:
struct AdornMaterial
{
shared_ptr<ShaderProgram> program;
int colorHandle;
int pixelInfoHandle;
AdornMaterial();
AdornMaterial(const shared_ptr<ShaderProgram>& program);
};
VisualEngine* visualEngine;
const DataModel* dataModel;
Aya::signal<void()> unbindResourcesSignal;
float currentHeight;
CoordinateFrame currentCFrame;
shared_ptr<Texture> currentTexture;
BatchTextureType currentTextureType;
Aya::signal<void()>& getUnbindResourcesSignal();
std::vector<AdornMesh> meshesOpaque;
std::vector<AdornMesh> meshesTransparent;
std::vector<AdornMesh> meshesNoDepthTest[Adorn::maximumZIndex + 1];
scoped_ptr<GeometryBatch> batchBox;
scoped_ptr<GeometryBatch> batchCylinderX;
scoped_ptr<GeometryBatch> batchSphere;
scoped_ptr<GeometryBatch> batchCone;
scoped_ptr<GeometryBatch> batchTorus;
scoped_ptr<GeometryBatch> batchAALineCylinderX;
AdornMaterial materials[Material_Count];
void submitMesh(const GeometryBatch& batch, Material material, const Vector3& translation, const Matrix3& rotation, const Vector3& scale,
const Color4& color, const Sphere& worldBounds, float thickness = 0, const int zIndex = -1, const bool alwaysOnTop = false);
void renderMeshes(DeviceContext* context, const std::vector<AdornMesh>& meshes, RenderPassStats& stats);
};
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,67 @@
#include "Render/CullableSceneNode.hpp"
#include "Render/SceneManager.hpp"
#include "Render/Util.hpp"
#include "Render/SpatialHashedScene.hpp"
#include "Render/VisualEngine.hpp"
#include "Base/FrameRateManager.hpp"
namespace Aya
{
namespace Graphics
{
CullableSceneNode::CullableSceneNode(VisualEngine* visualEngine, CullMode cullMode, unsigned int flags)
: visualEngine(visualEngine)
, cullMode(cullMode)
, flags(flags)
, blockCount(1)
, sqDistanceToFocus(0)
{
}
CullableSceneNode::~CullableSceneNode()
{
visualEngine->getSceneManager()->getSpatialHashedScene()->internalRemoveChild(this);
}
bool CullableSceneNode::updateIsCulledByFRM()
{
if (worldBounds.isNull())
return true;
const Vector3& focusPosition = visualEngine->getSceneManager()->getPointOfInterest();
sqDistanceToFocus = G3D::ClosestSqDistanceToAABB(focusPosition, worldBounds.center(), worldBounds.size() * 0.5f);
// count blocks and update farplane regardless of cullability.
if (sqDistanceToFocus > 1e-3f)
visualEngine->getSceneManager()->processSqPartDistance(sqDistanceToFocus);
Aya::FrameRateManager* frm = visualEngine->getFrameRateManager();
frm->AddBlockQuota(blockCount, sqDistanceToFocus, IsInSpatialHash());
// We don't do distance-cull on huge objects
if (cullMode == CullMode_SpatialHash && !IsInSpatialHash())
return false;
else
return sqDistanceToFocus > frm->GetRenderCullSqDistance();
}
void CullableSceneNode::updateRenderQueue(RenderQueue& queue, const RenderCamera& camera, RenderQueue::Pass pass) {}
void CullableSceneNode::updateWorldBounds(const Extents& aabb)
{
worldBounds = aabb;
if (aabb.isNull())
visualEngine->getSceneManager()->getSpatialHashedScene()->internalRemoveChild(this);
else
visualEngine->getSceneManager()->getSpatialHashedScene()->internalUpdateChild(this);
}
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,129 @@
#pragma once
#include "Base/GfxPart.hpp"
#include "Utility/Extents.hpp"
#include "RenderQueue.hpp"
namespace Aya
{
class Contact;
}
namespace Aya
{
namespace Graphics
{
class RenderCamera;
class RenderQueue;
class VisualEngine;
class SpatialHashNode;
class CullableSceneNode : public Aya::GfxPart
{
public:
static const bool hasGetFirstContact = false; // simulate __if_exists
static Aya::Contact* getContact(CullableSceneNode* p0, CullableSceneNode* p1)
{
AYAASSERT(0);
return NULL;
} // shouldn't be called if hasGetFirstContact is false
static Aya::Contact* getFirstContact()
{
AYAASSERT(NULL);
return NULL;
}
static Aya::Contact* getNextContact(Aya::Contact* prev)
{
AYAASSERT(NULL);
return NULL;
}
static int getNumContacts()
{
AYAASSERT(NULL);
return 0;
}
static CullableSceneNode* getContactOther(int id)
{
AYAASSERT(NULL);
return NULL;
}
bool requestFixed() const
{
return false;
}
const Extents& getFastFuzzyExtents()
{
return worldBounds;
}
public:
enum CullMode
{
CullMode_None,
CullMode_Simple,
CullMode_SpatialHash,
};
enum Flags
{
Flags_LightObject = 1 << 0,
Flags_ShadowCaster = 1 << 1,
};
CullableSceneNode(VisualEngine* visualEngine, CullMode cullMode, unsigned int flags);
~CullableSceneNode();
void setBlockCount(int blockCount)
{
this->blockCount = blockCount;
}
virtual void updateRenderQueue(RenderQueue& queue, const RenderCamera& camera, RenderQueue::Pass pass);
float getSqDistanceToFocus() const
{
return sqDistanceToFocus;
}
const Extents& getWorldBounds() const
{
return worldBounds;
}
void updateWorldBounds(const Extents& aabb);
VisualEngine* getVisualEngine() const
{
return visualEngine;
}
unsigned int getFlags() const
{
return flags;
}
bool updateIsCulledByFRM();
Vector3 getCenter() const override
{
return worldBounds.center();
}
private:
VisualEngine* visualEngine;
CullMode cullMode;
unsigned int flags;
int blockCount;
float sqDistanceToFocus;
Extents worldBounds;
};
} // namespace Graphics
} // namespace Aya

View File

@@ -0,0 +1,351 @@
#include "Render/CustomEmitter.hpp"
#include "DataModel/PartInstance.hpp"
#include "DataModel/CustomParticleEmitter.hpp"
#include "Render/SceneUpdater.hpp"
#include "Render/Util.hpp"
#include "Render/Emitter.hpp"
#include "Render/TextureManager.hpp"
#include "Render/VisualEngine.hpp"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
namespace Aya
{
namespace Graphics
{
CustomEmitter::CustomEmitter(VisualEngine* visualEngine)
: Super(visualEngine, CullMode_SpatialHash)
, dirtyFlags(0)
, enabled(false)
, localBox(Vector3::zero(), Vector3::zero())
, requestedEmitCount(0)
{
}
CustomEmitter::~CustomEmitter()
{
unbind();
// notify scene updater about destruction so that the pointer to CustomEmitter is no longer stored
getVisualEngine()->getSceneUpdater()->notifyDestroyed(this);
}
void CustomEmitter::onSleepingChangedEx(bool sleeping)
{
if (sleeping)
{
getVisualEngine()->getSceneUpdater()->notifySleeping(this);
}
else
{
getVisualEngine()->getSceneUpdater()->notifyAwake(this);
}
}
void CustomEmitter::updateCoordinateFrame(bool recalcLocalBounds)
{
CoordinateFrame frame = part ? part->calcRenderingCoordinateFrame() : CoordinateFrame();
if ((dirtyFlags & kDirty_Transform) == 0 && transform.fuzzyEq(frame))
{
return; // Nothing to update
}
transform = frame;
if (emitter)
{
emitter->transform() = transform;
localBox = emitter->computeBBox();
updateWorldBounds(localBox.toWorldSpace(transform));
if (part)
emitter->setVelocity(part->getVelocity());
else
emitter->setVelocity(Velocity());
}
}
void CustomEmitter::onCombinedSignalEx(Instance::CombinedSignalType type, const Instance::ICombinedSignalData* data)
{
switch (type)
{
case Instance::PROPERTY_CHANGED:
onPropertyChangedEx(boost::polymorphic_downcast<const Instance::PropertyChangedSignalData*>(data)->propertyDescriptor);
break;
case Instance::ANCESTRY_CHANGED:
onAncestorChangedEx();
break;
default:
break;
}
}
void CustomEmitter::onAncestorChangedEx()
{
shared_ptr<Instance> effectCopy = effect;
// Remove me from the scene if I am being removed from the Workspace
if (!isInWorkspace(effectCopy.get()))
{
// will cause a delete on next updateEntity()
zombify();
}
else
{
unbind();
Aya::PartInstance* parent = Aya::Instance::fastDynamicCast<Aya::PartInstance>(effectCopy->getParent());
shared_ptr<Aya::PartInstance> part = shared_from(parent);
bind(part, effectCopy);
}
}
void CustomEmitter::bind(const shared_ptr<Aya::PartInstance>& part, const shared_ptr<Aya::Instance>& instance)
{
AYAASSERT(!this->part && !this->effect);
this->part = part;
this->effect = shared_from(instance->fastDynamicCast<Aya::CustomParticleEmitter>());
//
if (part)
{
connections.push_back(part->onDemandWrite()->sleepingChangedSignal.connect(boost::bind(&CustomEmitter::onSleepingChangedEx, this, _1)));
// we just connected, so sync up the state.
onSleepingChangedEx(part->getSleeping());
}
if (effect)
{
connections.push_back(this->effect->onEmitRequested.connect(boost::bind(&CustomEmitter::requestEmit, this, _1)));
connections.push_back(instance->combinedSignal.connect(boost::bind(&CustomEmitter::onCombinedSignalEx, this, _1, _2)));
if (part)
{
connections.push_back(part->propertyChangedSignal.connect(boost::bind(&CustomEmitter::onParentSize, this, _1)));
}
}
dirtyFlags = kDirty_All;
invalidateEntity();
}
void CustomEmitter::unbind()
{
Super::unbind();
part.reset();
effect.reset();
}
void CustomEmitter::requestEmit(int particleCount)
{
requestedEmitCount += particleCount;
dirtyFlags |= kDirty_Fast;
invalidateEntity();
}
void CustomEmitter::invalidateEntity()
{
if ((dirtyFlags & kDirty_Invalidate) == 0)
{
dirtyFlags |= kDirty_Invalidate; // prevent repeated submissions
getVisualEngine()->getSceneUpdater()->queueInvalidatePart(this);
}
}
void CustomEmitter::updateEntity(bool assetsUpdated)
{
if (connections.empty()) // zombified.
{
getVisualEngine()->getSceneUpdater()->destroyAttachment(this);
return;
}
applyAllSettings();
updateCoordinateFrame(true);
dirtyFlags = kDirty_None;
}
void CustomEmitter::updateRenderQueue(RenderQueue& queue, const RenderCamera& camera, RenderQueue::Pass pass)
{
if (!enabled && emitter && !emitter->pcount())
emitter.reset();
if (emitter)
{
emitter->draw(queue);
}
// Render bounding box
if (getVisualEngine()->getSettings()->getDebugShowBoundingBoxes())
debugRenderBoundingBox();
}
static inline Vector2 v2(const NumberRange& r)
{
return Vector2(r.min, r.max);
}
void CustomEmitter::applyAllSettings()
{
if (!effect)
return;
if (!emitter)
{
emitter.reset(new Emitter(getVisualEngine(), true, effect->getFullName() + ".Texture"));
dirtyFlags = kDirty_All;
}
static const Vector2 zz(0, 0);
static const Vector3 zzz(0, 0, 0);
static const Vector4 zzzz(0, 0, 0, 0);
static const Vector2 cc(1, 1);
if (dirtyFlags & kDirty_Fast)
{
Vector3 boxsize(1, 1, 1);
if (part)
{
boxsize = 0.5f * part->getRenderSize();
}
enabled = effect->getEnabled();
emitter->setBlendRatio(1.0f - effect->getLightEmission());
emitter->setEmissionRate(effect->getRate() * effect->getEnabled());
emitter->setSpeed(v2(effect->getSpeed()));
emitter->setSpread(cc * effect->getSpread() / 180 * M_PI);
emitter->setRotation(v2(effect->getRotation()) / 180 * M_PI);
emitter->setSpin(v2(effect->getRotSpeed()) / 180 * M_PI);
emitter->setZOffset(effect->getZOffset());
emitter->setLife(v2(effect->getLifetime()));
emitter->setGlobalForce(effect->getAccel());
emitter->setEmitterShape(0, Box(-boxsize, boxsize));
emitter->setDampening(effect->getDampening());
emitter->setLockedToLocalSpace(effect->getLockedToLocalSpace());
emitter->setVelocityInheritance(effect->getVelocityInheritance());
localBox = emitter->computeBBox();
updateWorldBounds(localBox.toWorldSpace(transform));
// calculate the spherical direction
Vector2 sphericalDirection;
switch (effect->getEmissionDirection())
{
case Aya::NORM_Y:
sphericalDirection = Vector2(M_PI / 2, M_PI / 2);
break;
case Aya::NORM_Y_NEG:
sphericalDirection = Vector2(-M_PI / 2, M_PI / 2);
break;
case Aya::NORM_X:
sphericalDirection = Vector2(0, M_PI / 2);
break;
case Aya::NORM_X_NEG:
sphericalDirection = Vector2(0, -M_PI / 2);
break;
case Aya::NORM_Z_NEG:
sphericalDirection = Vector2(0, M_PI);
break;
case Aya::NORM_Z:
sphericalDirection = Vector2(0, 0);
break;
default:
break;
}
emitter->setSphericalDirection(sphericalDirection);
// emit!
if (requestedEmitCount > 0)
{
updateCoordinateFrame(true);
emitter->emit(requestedEmitCount);
requestedEmitCount = 0;
}
}
if (dirtyFlags & kDirty_Curves)
{
emitter->setColorCurve(&effect->getColor());
emitter->setAlphaCurve(&effect->getTransparency());
emitter->setSizeCurve(&effect->getSize());
localBox = emitter->computeBBox();
updateWorldBounds(localBox.toWorldSpace(transform));
}
if (dirtyFlags & kDirty_Slow)
{
Emitter::Appearance a = {};
a.alphaStripTexture = "";
a.colorStripTexture = "";
a.mainTexture = effect->getTexture().c_str();
a.colorStripBaseline = -1;
a.shader = Emitter::Shader_Custom;
a.blendCode = Emitter::Blend_PremultipliedAlpha;
emitter->setAppearance(a, effect->getFullName() + ".Texture");
}
}
void CustomEmitter::onPropertyChangedEx(const Aya::Reflection::PropertyDescriptor* p)
{
if (p == &CustomParticleEmitter::prop_texture)
{
dirtyFlags |= kDirty_Slow;
invalidateEntity();
return;
}
if (p == &CustomParticleEmitter::prop_color)
{
dirtyFlags |= kDirty_Curves;
invalidateEntity();
return;
}
if (p == &CustomParticleEmitter::prop_transp)
{
dirtyFlags |= kDirty_Curves;
invalidateEntity();
return;
}
if (p == &CustomParticleEmitter::prop_size)
{
dirtyFlags |= kDirty_Curves;
invalidateEntity();
return;
}
dirtyFlags |= kDirty_Fast;
invalidateEntity();
}
void CustomEmitter::onParentSize(pd p)
{
if (p == &PartInstance::prop_Size)
{
dirtyFlags |= kDirty_Fast;
invalidateEntity();
}
}
} // namespace Graphics
} // namespace Aya

Some files were not shown because too many files have changed in this diff Show More