Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8f5c63d
Preserve parent iOS deployment target
tryk016 Aug 30, 2026
c6a02d9
Harden iOS window lifetime
tryk016 Aug 30, 2026
d0c8dc2
Add safe iOS scene lifecycle
tryk016 Aug 30, 2026
74b4593
Prefer working directory files on iOS
tryk016 Aug 30, 2026
7c1de07
Add RG16F texture format
tryk016 Aug 30, 2026
1a26c08
Fix iOS autorelease-pool lifetime across fiber yields
tryk016 Aug 30, 2026
7c02b18
Add opt-in iOS display policy controls
tryk016 Aug 30, 2026
dc4cb21
Add portable spatial scaler API
tryk016 Aug 30, 2026
fe2e12a
Clamp iOS frame rates to the active screen
tryk016 Aug 30, 2026
691525b
Add configurable Metal swapchain buffer count
tryk016 Aug 30, 2026
4e77354
Add optional Metal shader module cache
tryk016 Aug 30, 2026
422b6d3
Preserve AbstractGraphicsApi vtable order
tryk016 Aug 30, 2026
6c5f63e
Add portable temporal scaler API
tryk016 Aug 30, 2026
066ef1e
Add optional MetalFX spatial scaler backend
tryk016 Aug 30, 2026
03e7568
Preserve parent Apple deployment target
tryk016 Aug 30, 2026
03fe9c1
Add optional MetalFX temporal scaler backend
tryk016 Aug 30, 2026
83675dc
Fix CTest registration for TempestTests
tryk016 Aug 30, 2026
0f958d5
Add optional direct Metal drawable rendering
tryk016 Aug 30, 2026
ab6777c
Add optional precompiled Metal shader libraries
tryk016 Aug 30, 2026
1160a93
Respect host iOS orientation policy
tryk016 Aug 30, 2026
179766f
Respect host iOS scene configuration
tryk016 Aug 30, 2026
ce76038
Fix scaler tests on MSVC
tryk016 Aug 30, 2026
cd0cf6b
Use shared host-visible Metal storage on iOS
tryk016 Aug 30, 2026
16fac30
Initialize brush state for triangle drawing
tryk016 Aug 30, 2026
047b949
Guard missing platform font fallback
tryk016 Aug 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Engine/2d/painter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ void Painter::implSetColor(float r, float g, float b, float a) {
void Painter::drawTriangle(int x0, int y0, float u0, float v0,
int x1, int y1, float u1, float v1,
int x2, int y2, float u2, float v2) {
if(state!=StBrush) {
dev.setTopology(Triangles);
state=StBrush;
implBrush(s.br);
}
FPoint trigBuf[4+4+4+4];
implDrawTrig( float(x0), float(y0), s.dU+u0*s.invW,s.dV+v0*s.invH,
float(x1), float(y1), s.dU+u1*s.invW,s.dV+v1*s.invH,
Expand All @@ -95,6 +100,11 @@ void Painter::drawTriangle(int x0, int y0, float u0, float v0,
void Painter::drawTriangle(float x0, float y0, float u0, float v0,
float x1, float y1, float u1, float v1,
float x2, float y2, float u2, float v2) {
if(state!=StBrush) {
dev.setTopology(Triangles);
state=StBrush;
implBrush(s.br);
}
FPoint trigBuf[4+4+4+4];
implDrawTrig( x0, y0, s.dU+u0*s.invW,s.dV+v0*s.invH,
x1, y1, s.dU+u1*s.invW,s.dV+v1*s.invH,
Expand Down
20 changes: 18 additions & 2 deletions Engine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ option(TEMPEST_BUILD_SHARED "Build shared Tempest." ON)

set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
set(CMAKE_OSX_DEPLOYMENT_TARGET 12.0)
if(NOT CMAKE_OSX_DEPLOYMENT_TARGET)
set(CMAKE_OSX_DEPLOYMENT_TARGET 12.0)
endif()

if(MSVC)
option(TEMPEST_BUILD_DIRECTX12 "Build directx12 support" ON )
Expand Down Expand Up @@ -188,6 +190,12 @@ if(TEMPEST_BUILD_METAL)
add_definitions(-DTEMPEST_BUILD_METAL)
add_subdirectory("thirdparty/metal-cpp" EXCLUDE_FROM_ALL)
target_include_directories(${PROJECT_NAME} PRIVATE "thirdparty/metal-cpp")
if(APPLE)
find_library(TEMPEST_METALFX_FRAMEWORK NAMES MetalFX)
if(TEMPEST_METALFX_FRAMEWORK)
target_compile_definitions(${PROJECT_NAME} PRIVATE TEMPEST_BUILD_METALFX)
endif()
endif()
endif()

### Spirv-cross
Expand Down Expand Up @@ -302,7 +310,9 @@ file(GLOB_RECURSE SOURCES
)

if(APPLE OR IOS)
set(CMAKE_OSX_DEPLOYMENT_TARGET 12.0)
if(NOT CMAKE_OSX_DEPLOYMENT_TARGET)
set(CMAKE_OSX_DEPLOYMENT_TARGET 12.0)
endif()
enable_language(OBJCXX)
file(GLOB_RECURSE ObjCSOURCES
"*.mm"
Expand All @@ -327,8 +337,14 @@ if(WIN32)
elseif(IOS)
#NOTE: use -DCMAKE_SYSTEM_NAME=iOS on first configure
target_link_libraries(${PROJECT_NAME} PRIVATE "-framework UiKit" "-framework Foundation" "-framework QuartzCore" "-framework Metal")
if(TEMPEST_METALFX_FRAMEWORK)
target_link_libraries(${PROJECT_NAME} PRIVATE "-weak_framework MetalFX")
endif()
elseif(APPLE)
target_link_libraries(${PROJECT_NAME} PRIVATE "-framework AppKit" "-framework QuartzCore" "-framework Metal")
if(TEMPEST_METALFX_FRAMEWORK)
target_link_libraries(${PROJECT_NAME} PRIVATE "-weak_framework MetalFX")
endif()
elseif(UNIX)
target_link_libraries(${PROJECT_NAME} PRIVATE X11 Xcursor)
endif()
Expand Down
11 changes: 9 additions & 2 deletions Engine/formats/font.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,15 @@ struct FontElement::Impl {
try {
std::lock_guard<std::mutex> guard(syncMap);
if(fallback==nullptr){
fallback.reset(new Impl(Detail::getFallbackFont()));
if(stbtt_InitFont(&fallback->info,fallback->data,0)==0)
const std::string path = Detail::getFallbackFont();
// Only Windows currently provides a platform fallback path. An empty
// Impl has no stb_truetype data, so passing its null buffer to
// stbtt_InitFont is undefined behaviour (and crashes on iOS/macOS).
if(path.empty())
return nullLater();
fallback.reset(new Impl(path));
if(fallback->data==nullptr || fallback->size==0 ||
stbtt_InitFont(&fallback->info,fallback->data,0)==0)
throw std::system_error(Tempest::SystemErrc::UnableToLoadAsset);
}

Expand Down
1 change: 1 addition & 0 deletions Engine/formats/image/pixmapcodeccommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ bool PixmapCodecCommon::save(ODevice &f, const char *ext, const uint8_t* cdata,
break;
case TextureFormat::R11G11B10UF:
case TextureFormat::RGBA16F:
case TextureFormat::RG16F:
// hdr or exr?
break;
}
Expand Down
3 changes: 3 additions & 0 deletions Engine/formats/pixmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ size_t Pixmap::blockSizeForFormat(TextureFormat frm) {
//---
case TextureFormat::R11G11B10UF: return 4;
case TextureFormat::RGBA16F: return 8;
case TextureFormat::RG16F: return 4;
}
return 0;
}
Expand Down Expand Up @@ -495,6 +496,7 @@ uint8_t Pixmap::componentCount(TextureFormat frm) {
//---
case TextureFormat::R11G11B10UF: return 3;
case TextureFormat::RGBA16F: return 4;
case TextureFormat::RG16F: return 2;
}
return 0;
}
Expand Down Expand Up @@ -526,6 +528,7 @@ Size Pixmap::blockCount(TextureFormat frm, uint32_t w, uint32_t h) {
case TextureFormat::Depth32F:
case TextureFormat::R11G11B10UF:
case TextureFormat::RGBA16F:
case TextureFormat::RG16F:
return Size(w,h);
case TextureFormat::DXT1:
case TextureFormat::DXT3:
Expand Down
19 changes: 19 additions & 0 deletions Engine/gapi/abstractgraphicsapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,15 @@ void AbstractGraphicsApi::CommandBuffer::dispatchMeshIndirect(const Buffer& indi
throw std::system_error(Tempest::GraphicsErrc::UnsupportedExtension);
}

bool AbstractGraphicsApi::CommandBuffer::spatialUpscale(SpatialScaler&, Texture&, Texture&) {
return false;
}

bool AbstractGraphicsApi::CommandBuffer::temporalUpscale(TemporalScaler&, Texture&, Texture&,
Texture&, Texture&, const TemporalScalerArgs&) {
return false;
}

AbstractGraphicsApi::AccelerationStructure* AbstractGraphicsApi::createBottomAccelerationStruct(Device* d, const RtGeometry* geom, size_t geomSize) {
throw std::system_error(Tempest::GraphicsErrc::UnsupportedExtension);
}
Expand All @@ -102,6 +111,16 @@ AbstractGraphicsApi::AccelerationStructure*
throw std::system_error(Tempest::GraphicsErrc::UnsupportedExtension);
}

AbstractGraphicsApi::SpatialScaler*
AbstractGraphicsApi::createSpatialScaler(Device*, const SpatialScalerDesc&) {
return nullptr;
}

AbstractGraphicsApi::TemporalScaler*
AbstractGraphicsApi::createTemporalScaler(Device*, const TemporalScalerDesc&) {
return nullptr;
}

bool Detail::Bindings::operator ==(const Bindings &other) const {
for(size_t i=0; i<MaxBindings; ++i) {
if(data[i]!=other.data[i])
Expand Down
47 changes: 47 additions & 0 deletions Engine/gapi/abstractgraphicsapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ namespace Tempest {
DXT5,
R11G11B10UF,
RGBA16F,
RG16F,
Last
};

Expand Down Expand Up @@ -159,6 +160,7 @@ namespace Tempest {
case DXT5: return "DXT5";
case R11G11B10UF: return "R11G11B10UF";
case RGBA16F: return "RGBA16F";
case RG16F: return "RG16F";
case Last:
break;
}
Expand All @@ -173,6 +175,43 @@ namespace Tempest {
return f==TextureFormat::DXT1 || f==TextureFormat::DXT3 || f==TextureFormat::DXT5;
}

enum class SpatialScalerColorMode : uint8_t {
Perceptual,
Linear,
HDR,
};

struct SpatialScalerDesc final {
TextureFormat inputFormat = Undefined;
TextureFormat outputFormat = Undefined;
uint32_t inputWidth = 0;
uint32_t inputHeight = 0;
uint32_t outputWidth = 0;
uint32_t outputHeight = 0;
SpatialScalerColorMode colorMode = SpatialScalerColorMode::Perceptual;
};

struct TemporalScalerDesc final {
TextureFormat inputFormat = Undefined;
TextureFormat depthFormat = Undefined;
TextureFormat motionFormat = Undefined;
TextureFormat outputFormat = Undefined;
uint32_t inputWidth = 0;
uint32_t inputHeight = 0;
uint32_t outputWidth = 0;
uint32_t outputHeight = 0;
bool autoExposure = true;
};

struct TemporalScalerArgs final {
float jitterOffsetX = 0.f;
float jitterOffsetY = 0.f;
float motionVectorScaleX = 1.f;
float motionVectorScaleY = 1.f;
bool resetHistory = false;
bool depthReversed = false;
};

enum class ComponentSwizzle {
Identity = 0,
R,
Expand Down Expand Up @@ -558,6 +597,8 @@ namespace Tempest {
};
struct BlasBuildCtx {};
struct AccelerationStructure:Shared {};
struct SpatialScaler:NoCopy {};
struct TemporalScaler:NoCopy {};
struct DescArray:NoCopy {};
struct BarrierDesc {
const Texture* texture = nullptr;
Expand Down Expand Up @@ -618,6 +659,10 @@ namespace Tempest {

virtual void dispatch(size_t x, size_t y, size_t z) = 0;
virtual void dispatchIndirect(const Buffer& indirect, size_t offset) = 0;

virtual bool spatialUpscale(SpatialScaler& scaler, Texture& input, Texture& output);
virtual bool temporalUpscale(TemporalScaler& scaler, Texture& input, Texture& depth,
Texture& motion, Texture& output, const TemporalScalerArgs& args);
};

using PBuffer = Detail::DSharedPtr<Buffer*>;
Expand Down Expand Up @@ -662,6 +707,8 @@ namespace Tempest {
virtual auto submit (Device *d, CommandBuffer* cmd) -> std::shared_ptr<AbstractGraphicsApi::Fence> = 0;

virtual void getCaps(Device *d, Props& caps)=0;
virtual SpatialScaler* createSpatialScaler(Device* d, const SpatialScalerDesc& desc);
virtual TemporalScaler* createTemporalScaler(Device* d, const TemporalScalerDesc& desc);

friend class Tempest::Device;
};
Expand Down
2 changes: 1 addition & 1 deletion Engine/gapi/directx12/dxcommandbuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,7 @@ DxCompPipeline& DxCommandBuffer::copyShader(DXGI_FORMAT format, int32_t& bitCnt,
case DXGI_FORMAT_R16G16_UINT:
case DXGI_FORMAT_R16G16_SNORM:
case DXGI_FORMAT_R16G16_SINT:
case DXGI_FORMAT_R16G16_FLOAT:
bitCnt = 16;
compCnt = 2;
return *dev.copyS.handler;
Expand Down Expand Up @@ -1341,4 +1342,3 @@ void DxCommandBuffer::generateMipmap(AbstractGraphicsApi::Texture& dstTex,

#endif


2 changes: 2 additions & 0 deletions Engine/gapi/directx12/dxdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ inline DXGI_FORMAT nativeFormat(TextureFormat f) {
return DXGI_FORMAT_R11G11B10_FLOAT;
case TextureFormat::RGBA16F:
return DXGI_FORMAT_R16G16B16A16_FLOAT;
case TextureFormat::RG16F:
return DXGI_FORMAT_R16G16_FLOAT;
}
return DXGI_FORMAT_UNKNOWN;
}
Expand Down
6 changes: 6 additions & 0 deletions Engine/gapi/metal/metal_cpp.mm
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
#define NS_PRIVATE_IMPLEMENTATION
#define CA_PRIVATE_IMPLEMENTATION
#define MTL_PRIVATE_IMPLEMENTATION
#if defined(TEMPEST_BUILD_METALFX)
#define MTLFX_PRIVATE_IMPLEMENTATION
#endif
#include <Foundation/Foundation.hpp>
#include <Metal/Metal.hpp>
#include <QuartzCore/QuartzCore.hpp>
#if defined(TEMPEST_BUILD_METALFX)
#include <MetalFX/MetalFX.hpp>
#endif
6 changes: 3 additions & 3 deletions Engine/gapi/metal/mtaccelerationstructure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ MtTopAccelerationStructure::MtTopAccelerationStructure(MtDevice& dx, const RtIns
:owner(dx) {
auto pool = NsPtr<NS::AutoreleasePool>::init();
instances = NsPtr<MTL::Buffer>(dx.impl->newBuffer(sizeof(MTL::AccelerationStructureUserIDInstanceDescriptor)*asSize,
MTL::ResourceStorageModeManaged));
hostVisibleResourceOptions(*dx.impl)));
if(instances==nullptr)
throw std::system_error(GraphicsErrc::OutOfVideoMemory);

Expand Down Expand Up @@ -128,7 +128,8 @@ MtTopAccelerationStructure::MtTopAccelerationStructure(MtDevice& dx, const RtIns
blas.push_back(ax->impl.get());
}
}
instances->didModifyRange(NS::Range(0,instances->length()));
if(instances->storageMode()==MTL::StorageModeManaged)
instances->didModifyRange(NS::Range(0,instances->length()));

auto asArray = NsPtr<NS::Array>(NS::Array::array(reinterpret_cast<NS::Object**>(blas.data()), blas.size()));
auto desc = NsPtr<MTL::InstanceAccelerationStructureDescriptor>::init();
Expand Down Expand Up @@ -196,4 +197,3 @@ void MtTopAccelerationStructure::implUseResource(MTL::RenderCommandEncoder& cmd,
}

#endif

49 changes: 48 additions & 1 deletion Engine/gapi/metal/mtcommandbuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@
#include "mttexture.h"
#include "mtswapchain.h"
#include "mtaccelerationstructure.h"
#if defined(TEMPEST_BUILD_METALFX)
#include "mtspatialscaler.h"
#endif
#if defined(TEMPEST_BUILD_METALFX_TEMPORAL)
#include "mttemporalscaler.h"
#endif

using namespace Tempest;
using namespace Tempest::Detail;
Expand Down Expand Up @@ -65,6 +71,7 @@ void MtCommandBuffer::end() {
}

void MtCommandBuffer::reset() {
swapchainFrames.clear();
auto pool = NsPtr<NS::AutoreleasePool>::init();
auto desc = NsPtr<MTL::CommandBufferDescriptor>::init();
desc->setRetainedReferences(false);
Expand Down Expand Up @@ -94,7 +101,13 @@ void MtCommandBuffer::beginRendering(const FrameBufferDesc& fbo, size_t fboSize,
auto clr = desc->colorAttachments()->object(i);
if(fbo.sw[i]!=nullptr) {
auto& s = *reinterpret_cast<MtSwapchain*>(fbo.sw[i]);
clr->setTexture(s.img[fbo.imgId[i]].tex.get());
auto frame = s.acquireRenderTarget(fbo.imgId[i]);
clr->setTexture(frame->texture.get());
bool known = false;
for(auto& existing:swapchainFrames)
known = known || existing.get()==frame.get();
if(!known)
swapchainFrames.push_back(std::move(frame));
curFbo.colorFormat[curFbo.numColors] = s.format();
} else {
auto& t = *reinterpret_cast<MtTexture*>(fbo.att[i]);
Expand Down Expand Up @@ -796,4 +809,38 @@ void MtCommandBuffer::copy(AbstractGraphicsApi::Buffer& dest, size_t offset,
offset, bpp*width,bpp*width*height);
}

#if defined(TEMPEST_BUILD_METALFX)
bool MtCommandBuffer::spatialUpscale(AbstractGraphicsApi::SpatialScaler& scaler,
AbstractGraphicsApi::Texture& input,
AbstractGraphicsApi::Texture& output) {
setEncoder(E_None,nullptr);
auto* sx = dynamic_cast<MtSpatialScaler*>(&scaler);
auto* src = dynamic_cast<MtTexture*>(&input);
auto* dst = dynamic_cast<MtTexture*>(&output);
if(sx==nullptr || src==nullptr || dst==nullptr || !sx->belongsTo(device))
return false;
return sx->encode(*impl,*src,*dst);
}
#endif

#if defined(TEMPEST_BUILD_METALFX_TEMPORAL)
bool MtCommandBuffer::temporalUpscale(AbstractGraphicsApi::TemporalScaler& scaler,
AbstractGraphicsApi::Texture& input,
AbstractGraphicsApi::Texture& depth,
AbstractGraphicsApi::Texture& motion,
AbstractGraphicsApi::Texture& output,
const TemporalScalerArgs& args) {
setEncoder(E_None,nullptr);
auto* sx = dynamic_cast<MtTemporalScaler*>(&scaler);
auto* src = dynamic_cast<MtTexture*>(&input);
auto* dep = dynamic_cast<MtTexture*>(&depth);
auto* mov = dynamic_cast<MtTexture*>(&motion);
auto* dst = dynamic_cast<MtTexture*>(&output);
if(sx==nullptr || src==nullptr || dep==nullptr || mov==nullptr || dst==nullptr ||
!sx->belongsTo(device))
return false;
return sx->encode(*impl,*src,*dep,*mov,*dst,args);
}
#endif

#endif
Loading