You've already forked llvm-project
mirror of
https://github.com/encounter/llvm-project.git
synced 2026-03-30 11:27:19 -07:00
625 lines
18 KiB
C++
625 lines
18 KiB
C++
#define HSH_IMPLEMENTATION
|
|
|
|
#include <chrono>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <hsh/hsh.h>
|
|
#include <iostream>
|
|
#include <string_view>
|
|
using namespace std::literals;
|
|
|
|
#include "test-input.h"
|
|
#include "urde-test.h"
|
|
|
|
constexpr std::string_view AppName = "Hello Triangle"sv;
|
|
|
|
#if !HSH_PROFILE_MODE
|
|
|
|
#if _WIN32
|
|
|
|
#define NOMINMAX
|
|
#include <Windows.h>
|
|
|
|
struct WsiConnection;
|
|
struct WsiWindow {
|
|
WsiConnection &Connection;
|
|
HWND Window;
|
|
explicit WsiWindow(WsiConnection &Connection);
|
|
~WsiWindow();
|
|
};
|
|
|
|
struct WsiConnection {
|
|
HINSTANCE Instance;
|
|
bool Running = true;
|
|
|
|
operator HINSTANCE() const { return Instance; }
|
|
|
|
static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
|
|
LPARAM lParam) {
|
|
if (msg == WM_DESTROY)
|
|
PostQuitMessage(0);
|
|
return DefWindowProc(hWnd, msg, wParam, lParam);
|
|
}
|
|
|
|
WsiConnection() {
|
|
Instance = GetModuleHandle(nullptr);
|
|
assert(Instance);
|
|
|
|
WNDCLASSEX Class{};
|
|
|
|
Class.cbSize = sizeof(Class);
|
|
Class.style = 0;
|
|
Class.lpfnWndProc = &WindowProc;
|
|
Class.cbClsExtra = 0;
|
|
Class.cbWndExtra = 0;
|
|
Class.hInstance = Instance;
|
|
Class.hIcon = nullptr;
|
|
Class.hCursor = nullptr;
|
|
Class.hbrBackground = HBRUSH(GetStockObject(BLACK_BRUSH));
|
|
Class.lpszMenuName = nullptr;
|
|
Class.lpszClassName = TEXT("TestappWndClass");
|
|
Class.hIconSm = nullptr;
|
|
|
|
RegisterClassEx(&Class);
|
|
}
|
|
|
|
WsiWindow makeWindow() { return WsiWindow(*this); }
|
|
|
|
void _handleMsg(const MSG &Msg) {
|
|
switch (Msg.message) {
|
|
case WM_QUIT:
|
|
Running = false;
|
|
break;
|
|
default:
|
|
TranslateMessage(&Msg);
|
|
DispatchMessage(&Msg);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void dispatchLatestEvents() {
|
|
MSG Msg;
|
|
while (PeekMessage(&Msg, nullptr, 0, 0, PM_REMOVE))
|
|
_handleMsg(Msg);
|
|
}
|
|
|
|
void runloop(const std::function<bool()> &IdleFunc) {
|
|
while (Running) {
|
|
dispatchLatestEvents();
|
|
if (!IdleFunc()) {
|
|
Running = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
WsiWindow::WsiWindow(WsiConnection &Connection) : Connection(Connection) {
|
|
RECT Rect{0, 0, 512, 512};
|
|
AdjustWindowRect(&Rect, WS_OVERLAPPEDWINDOW, false);
|
|
Window = CreateWindow(TEXT("TestappWndClass"), TEXT("HSH Test App"),
|
|
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
|
|
Rect.right - Rect.left, Rect.bottom - Rect.top,
|
|
(HWND) nullptr, (HMENU) nullptr, Connection,
|
|
(LPVOID) nullptr);
|
|
ShowWindow(Window, SW_SHOW);
|
|
}
|
|
|
|
WsiWindow::~WsiWindow() { DestroyWindow(Window); }
|
|
|
|
struct PipelineCacheFileManager {
|
|
static std::wstring GetFilename(const uint8_t UUID[VK_UUID_SIZE]) noexcept {
|
|
std::wostringstream FileName;
|
|
// TODO: make this more portable
|
|
wchar_t LocalAppData[MAX_PATH];
|
|
if (GetEnvironmentVariable(TEXT("LOCALAPPDATA"), LocalAppData, MAX_PATH)) {
|
|
FileName << LocalAppData;
|
|
FileName << TEXT("/cache/hsh-test-pipeline-cache-");
|
|
for (int i = 0; i < VK_UUID_SIZE; ++i)
|
|
FileName << std::hex << unsigned(UUID[i]);
|
|
FileName << TEXT(".bin");
|
|
}
|
|
return FileName.str();
|
|
}
|
|
|
|
template <typename Func>
|
|
static void ReadPipelineCache(Func F,
|
|
const uint8_t UUID[VK_UUID_SIZE]) noexcept {
|
|
if (std::FILE *File = ::_wfopen(GetFilename(UUID).c_str(), TEXT("rb"))) {
|
|
std::fseek(File, 0, SEEK_END);
|
|
auto Size = std::ftell(File);
|
|
if (Size != 0) {
|
|
std::fseek(File, 0, SEEK_SET);
|
|
std::unique_ptr<uint8_t[]> Data(new uint8_t[Size]);
|
|
Size = std::fread(Data.get(), 1, Size, File);
|
|
if (Size != 0)
|
|
F(Data.get(), Size);
|
|
}
|
|
std::fclose(File);
|
|
}
|
|
}
|
|
|
|
template <typename Func>
|
|
static void WritePipelineCache(Func F,
|
|
const uint8_t UUID[VK_UUID_SIZE]) noexcept {
|
|
if (std::FILE *File = ::_wfopen(GetFilename(UUID).c_str(), TEXT("wb"))) {
|
|
F([File](const uint8_t *Data, std::size_t Size) {
|
|
std::fwrite(Data, 1, Size, File);
|
|
});
|
|
std::fclose(File);
|
|
}
|
|
}
|
|
};
|
|
|
|
#elif defined(__APPLE__)
|
|
|
|
#include <AppKit/AppKit.h>
|
|
#include <QuartzCore/CoreAnimation.h>
|
|
|
|
@interface MetalView : NSView
|
|
- (id)initWithFrame:(NSRect)frameRect;
|
|
- (CALayer *)makeBackingLayer;
|
|
@end
|
|
|
|
@implementation MetalView
|
|
- (id)initWithFrame:(NSRect)frameRect {
|
|
self = [super initWithFrame:frameRect];
|
|
self.wantsLayer = YES;
|
|
return self;
|
|
}
|
|
- (CALayer *)makeBackingLayer {
|
|
CAMetalLayer *Layer = [CAMetalLayer layer];
|
|
Layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
|
|
return Layer;
|
|
}
|
|
- (BOOL)layer:(CALayer *)layer
|
|
shouldInheritContentsScale:(CGFloat)newScale
|
|
fromWindow:(NSWindow *)window {
|
|
return YES;
|
|
}
|
|
@end
|
|
|
|
@interface CocoaWindow : NSWindow
|
|
- (id)init;
|
|
@end
|
|
|
|
@implementation CocoaWindow
|
|
- (id)init {
|
|
NSRect ContentRect = NSMakeRect(0, 0, 512, 512);
|
|
self = [super
|
|
initWithContentRect:ContentRect
|
|
styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
|
|
NSWindowStyleMaskMiniaturizable |
|
|
NSWindowStyleMaskResizable /*|
|
|
NSWindowStyleMaskFullSizeContentView*/
|
|
backing:NSBackingStoreBuffered
|
|
defer:NO];
|
|
self.contentView = [[MetalView alloc] initWithFrame:ContentRect];
|
|
return self;
|
|
}
|
|
@end
|
|
|
|
struct WsiConnection;
|
|
struct WsiWindow {
|
|
WsiConnection &Connection;
|
|
CocoaWindow *Window;
|
|
explicit WsiWindow(WsiConnection &Connection);
|
|
~WsiWindow();
|
|
};
|
|
|
|
@interface Application : NSApplication <NSApplicationDelegate>
|
|
@end
|
|
|
|
@implementation Application
|
|
- (id)init {
|
|
self = [super init];
|
|
self.activationPolicy = NSApplicationActivationPolicyRegular;
|
|
self.delegate = self;
|
|
|
|
NSMenu *Menu = [NSMenu new];
|
|
NSMenuItem *MenuItem = [NSMenuItem new];
|
|
[Menu addItem:MenuItem];
|
|
NSMenu *AppMenu = [NSMenu new];
|
|
NSString *AppName = [[NSProcessInfo processInfo] processName];
|
|
NSString *QuitTitle = [@"Quit " stringByAppendingString:AppName];
|
|
NSMenuItem *QuitMenuItem =
|
|
[[NSMenuItem alloc] initWithTitle:QuitTitle
|
|
action:@selector(terminate:)
|
|
keyEquivalent:@"q"];
|
|
[AppMenu addItem:QuitMenuItem];
|
|
[MenuItem setSubmenu:AppMenu];
|
|
self.mainMenu = Menu;
|
|
|
|
return self;
|
|
}
|
|
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:
|
|
(NSApplication *)sender {
|
|
return YES;
|
|
}
|
|
- (void)terminate:(id)sender {
|
|
/* Use -stop in place of -terminate so -run returns */
|
|
[self stop:sender];
|
|
}
|
|
@end
|
|
|
|
struct WsiConnection {
|
|
Application *App;
|
|
|
|
operator NSApplication *() const { return App; }
|
|
|
|
WsiConnection() { App = [Application sharedApplication]; }
|
|
|
|
WsiWindow makeWindow() { return WsiWindow(*this); }
|
|
|
|
const std::function<bool()> *IdleFuncPtr = nullptr;
|
|
|
|
void idle() {
|
|
if (!(*IdleFuncPtr)())
|
|
[App stop:nil];
|
|
if (App.isRunning)
|
|
dispatch_async_f(dispatch_get_main_queue(), this,
|
|
dispatch_function_t(_idle));
|
|
}
|
|
static void _idle(WsiConnection *Self) { Self->idle(); }
|
|
|
|
void dispatchLatestEvents() {
|
|
while (NSEvent *Event = [App nextEventMatchingMask:NSEventMaskAny
|
|
untilDate:nullptr
|
|
inMode:NSDefaultRunLoopMode
|
|
dequeue:YES])
|
|
[App sendEvent:Event];
|
|
}
|
|
|
|
void runloop(const std::function<bool()> &IdleFunc) {
|
|
IdleFuncPtr = &IdleFunc;
|
|
dispatch_async_f(dispatch_get_main_queue(), this,
|
|
dispatch_function_t(_idle));
|
|
[App run];
|
|
IdleFuncPtr = nullptr;
|
|
}
|
|
};
|
|
|
|
WsiWindow::WsiWindow(WsiConnection &Connection) : Connection(Connection) {
|
|
Window = [CocoaWindow new];
|
|
[Window makeKeyAndOrderFront:nil];
|
|
}
|
|
|
|
WsiWindow::~WsiWindow() { [Window close]; }
|
|
|
|
struct PipelineCacheFileManager {
|
|
static NSURL *GetFilename() noexcept {
|
|
std::ostringstream FileName;
|
|
// TODO: make this more portable
|
|
if (const char *home = std::getenv("HOME")) {
|
|
FileName << home;
|
|
FileName << "/Library/Caches/hsh-test-pipeline-cache.bin";
|
|
}
|
|
return [NSURL fileURLWithPath:@(FileName.str().c_str())];
|
|
}
|
|
|
|
template <typename Func> static void ReadPipelineCache(Func F) noexcept {
|
|
F(GetFilename());
|
|
}
|
|
|
|
template <typename Func> static void WritePipelineCache(Func F) noexcept {
|
|
F(GetFilename());
|
|
}
|
|
};
|
|
|
|
#else
|
|
|
|
struct WsiConnection;
|
|
struct WsiWindow {
|
|
WsiConnection &Connection;
|
|
xcb_window_t Window;
|
|
explicit WsiWindow(WsiConnection &Connection);
|
|
~WsiWindow();
|
|
};
|
|
|
|
struct WsiConnection {
|
|
xcb_connection_t *Connection;
|
|
xcb_atom_t wmDeleteWin, wmProtocols;
|
|
bool Running = true;
|
|
|
|
operator xcb_connection_t *() const { return Connection; }
|
|
|
|
WsiConnection() {
|
|
Connection = xcb_connect(nullptr, nullptr);
|
|
assert(Connection);
|
|
|
|
xcb_intern_atom_cookie_t wmDeleteCookie = xcb_intern_atom(
|
|
Connection, 0, strlen("WM_DELETE_WINDOW"), "WM_DELETE_WINDOW");
|
|
xcb_intern_atom_cookie_t wmProtocolsCookie =
|
|
xcb_intern_atom(Connection, 0, strlen("WM_PROTOCOLS"), "WM_PROTOCOLS");
|
|
|
|
xcb_intern_atom_reply_t *wmDeleteReply =
|
|
xcb_intern_atom_reply(Connection, wmDeleteCookie, nullptr);
|
|
xcb_intern_atom_reply_t *wmProtocolsReply =
|
|
xcb_intern_atom_reply(Connection, wmProtocolsCookie, nullptr);
|
|
|
|
wmDeleteWin = wmDeleteReply->atom;
|
|
wmProtocols = wmProtocolsReply->atom;
|
|
}
|
|
|
|
WsiWindow makeWindow() { return WsiWindow(*this); }
|
|
|
|
void _handleEvent(xcb_generic_event_t *event) {
|
|
switch (event->response_type & ~0x80u) {
|
|
case XCB_CLIENT_MESSAGE: {
|
|
auto *cm = (xcb_client_message_event_t *)event;
|
|
|
|
if (cm->data.data32[0] == wmDeleteWin)
|
|
Running = false;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void dispatchLatestEvents() {
|
|
while (xcb_generic_event_t *event = xcb_poll_for_event(Connection)) {
|
|
_handleEvent(event);
|
|
free(event);
|
|
}
|
|
}
|
|
|
|
void runloop(const std::function<bool()> &IdleFunc) {
|
|
while (Running) {
|
|
dispatchLatestEvents();
|
|
if (!IdleFunc()) {
|
|
Running = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
WsiWindow::WsiWindow(WsiConnection &Connection) : Connection(Connection) {
|
|
const struct xcb_setup_t *Setup = xcb_get_setup(Connection);
|
|
xcb_screen_iterator_t Screen = xcb_setup_roots_iterator(Setup);
|
|
assert(Screen.rem);
|
|
|
|
Window = xcb_generate_id(Connection);
|
|
uint32_t EventMask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
|
|
uint32_t ValueList[] = {Screen.data->black_pixel, 0};
|
|
xcb_create_window(Connection, XCB_COPY_FROM_PARENT, Window, Screen.data->root,
|
|
0, 0, 512, 512, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
|
|
Screen.data->root_visual, EventMask, ValueList);
|
|
xcb_change_property(Connection, XCB_PROP_MODE_REPLACE, Window,
|
|
XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, AppName.size(),
|
|
AppName.data());
|
|
xcb_change_property(Connection, XCB_PROP_MODE_REPLACE, Window,
|
|
Connection.wmProtocols, 4, 32, 1,
|
|
&Connection.wmDeleteWin);
|
|
|
|
xcb_map_window(Connection, Window);
|
|
xcb_flush(Connection);
|
|
}
|
|
|
|
WsiWindow::~WsiWindow() { xcb_destroy_window(Connection, Window); }
|
|
|
|
struct PipelineCacheFileManager {
|
|
static std::string GetFilename(const uint8_t UUID[VK_UUID_SIZE]) noexcept {
|
|
std::ostringstream FileName;
|
|
// TODO: make this more portable
|
|
if (const char *home = std::getenv("HOME")) {
|
|
FileName << home;
|
|
FileName << "/.cache/hsh-test-pipeline-cache-";
|
|
for (int i = 0; i < VK_UUID_SIZE; ++i)
|
|
FileName << std::hex << unsigned(UUID[i]);
|
|
FileName << ".bin";
|
|
}
|
|
return FileName.str();
|
|
}
|
|
|
|
template <typename Func>
|
|
static void ReadPipelineCache(Func F,
|
|
const uint8_t UUID[VK_UUID_SIZE]) noexcept {
|
|
if (std::FILE *File = std::fopen(GetFilename(UUID).c_str(), "rb")) {
|
|
std::fseek(File, 0, SEEK_END);
|
|
auto Size = std::ftell(File);
|
|
if (Size != 0) {
|
|
std::fseek(File, 0, SEEK_SET);
|
|
std::unique_ptr<uint8_t[]> Data(new uint8_t[Size]);
|
|
Size = std::fread(Data.get(), 1, Size, File);
|
|
if (Size != 0)
|
|
F(Data.get(), Size);
|
|
}
|
|
std::fclose(File);
|
|
}
|
|
}
|
|
|
|
template <typename Func>
|
|
static void WritePipelineCache(Func F,
|
|
const uint8_t UUID[VK_UUID_SIZE]) noexcept {
|
|
if (std::FILE *File = std::fopen(GetFilename(UUID).c_str(), "wb")) {
|
|
F([File](const uint8_t *Data, std::size_t Size) {
|
|
std::fwrite(Data, 1, Size, File);
|
|
});
|
|
std::fclose(File);
|
|
}
|
|
}
|
|
};
|
|
|
|
#endif
|
|
|
|
#if _WIN32
|
|
int wmain(int argc, wchar_t **argv) {
|
|
#else
|
|
int main(int argc, char **argv) {
|
|
#endif
|
|
WsiConnection Connection;
|
|
WsiWindow Window = Connection.makeWindow();
|
|
|
|
#ifndef __APPLE__
|
|
|
|
auto Instance = hsh::create_vulkan_instance(
|
|
AppName.data(), 0, "test-engine", 0,
|
|
[](vk::DebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
|
vk::DebugUtilsMessageTypeFlagBitsEXT messageTypes,
|
|
const vk::DebugUtilsMessengerCallbackDataEXT &pCallbackData) {
|
|
std::cerr << to_string(messageSeverity) << " "
|
|
<< to_string(messageTypes) << " " << pCallbackData.pMessage
|
|
<< "\n";
|
|
});
|
|
if (!Instance)
|
|
return 1;
|
|
|
|
Instance.enumerate_vulkan_devices(
|
|
[](const vk::PhysicalDeviceProperties &Props,
|
|
const vk::PhysicalDeviceDriverProperties &DriverProps) {
|
|
std::cerr << "name: " << Props.deviceName
|
|
<< " type: " << vk::to_string(Props.deviceType) << "\n";
|
|
return false;
|
|
});
|
|
|
|
auto PhysSurface = Instance.create_phys_surface(Connection, Window.Window);
|
|
if (!PhysSurface) {
|
|
std::cerr << "Unable to create PhysSurface\n";
|
|
return 1;
|
|
}
|
|
|
|
auto Device = Instance.enumerate_vulkan_devices(
|
|
[&](const vk::PhysicalDeviceProperties &Props,
|
|
const vk::PhysicalDeviceDriverProperties &DriverProps) {
|
|
return true;
|
|
},
|
|
*PhysSurface);
|
|
if (!Device) {
|
|
std::cerr << "No vulkan devices found\n";
|
|
return 1;
|
|
}
|
|
|
|
#else
|
|
|
|
auto Instance = hsh::create_metal_instance(
|
|
[](NSError *Err) { std::cerr << Err.localizedDescription.UTF8String; });
|
|
if (!Instance)
|
|
return 1;
|
|
|
|
Instance.enumerate_metal_devices(
|
|
[](id<MTLDevice> Device, bool NativeForPhysSurface) {
|
|
std::cerr << "name: " << Device.name.UTF8String << "\n";
|
|
return false;
|
|
});
|
|
|
|
CAMetalLayer *PhysSurface = (CAMetalLayer *)Window.Window.contentView.layer;
|
|
|
|
auto Device = Instance.enumerate_metal_devices(
|
|
[&](id<MTLDevice> Device, bool NativeForPhysSurface) {
|
|
return NativeForPhysSurface;
|
|
},
|
|
PhysSurface);
|
|
if (!Device) {
|
|
std::cerr << "No metal devices found\n";
|
|
return 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
auto Surface = hsh::create_surface(std::move(PhysSurface));
|
|
if (!Surface) {
|
|
std::cerr << "PhysSurface not compatible\n";
|
|
return 1;
|
|
}
|
|
|
|
auto RenderTexture = hsh::create_render_texture2d(Surface);
|
|
|
|
{
|
|
hsh::ShaderFileMapper SFM;
|
|
if (!SFM.Good) {
|
|
std::cerr << "Unable to map shader data\n";
|
|
return 1;
|
|
}
|
|
PipelineCacheFileManager PCFM;
|
|
Device.build_pipelines(PCFM);
|
|
}
|
|
|
|
MyNS::Binding PipelineBind{};
|
|
MyNS::Binding PipelineTemplate1Bind{};
|
|
MyNS::Binding PipelineTemplate2Bind{};
|
|
MyNS::Binding PipelineTemplate3Bind{};
|
|
|
|
ModelResources ModRes{};
|
|
hsh::binding ModelBinding;
|
|
hsh::uniform_fifo UFifo;
|
|
hsh::vertex_fifo VFifo;
|
|
|
|
std::size_t CurColor = 0;
|
|
constexpr std::array<hsh::float4, 7> Rainbow{{{1.f, 0.f, 0.f, 1.f},
|
|
{1.f, 0.5f, 0.f, 1.f},
|
|
{1.f, 1.f, 0.f, 1.f},
|
|
{0.f, 1.f, 0.f, 1.f},
|
|
{0.f, 1.f, 1.f, 1.f},
|
|
{0.f, 0.f, 1.f, 1.f},
|
|
{0.5f, 0.f, 1.f, 1.f}}};
|
|
|
|
Connection.runloop([&]() {
|
|
Device.enter_draw_context([&]() {
|
|
if (!UFifo)
|
|
UFifo = hsh::create_uniform_fifo(
|
|
256 +
|
|
sizeof(
|
|
MyNS::UniformData)); // Maximum alignment by vulkan spec is 256
|
|
if (!VFifo)
|
|
VFifo = hsh::create_vertex_fifo(sizeof(MyNS::MyFormat) * 3 * 2);
|
|
|
|
if (Surface.acquire_next_image()) {
|
|
Connection.dispatchLatestEvents();
|
|
RenderTexture.attach();
|
|
hsh::clear_attachments();
|
|
|
|
auto UFifoBinding =
|
|
UFifo.map<MyNS::UniformData>([&](MyNS::UniformData &UniData) {
|
|
UniData = MyNS::UniformData{};
|
|
UniData.xf[0][0] = 1.f;
|
|
UniData.xf[1][1] = 1.f;
|
|
UniData.xf[2][2] = 1.f;
|
|
UniData.xf[3][3] = 1.f;
|
|
// UniData.color = Rainbow[CurColor];
|
|
UniData.color = hsh::float4(1.f, 1.f, 1.f, 1.f);
|
|
});
|
|
auto VFifoBinding =
|
|
VFifo.map<MyNS::MyFormat>(3, [&](MyNS::MyFormat *VertData) {
|
|
VertData[0] = MyNS::MyFormat{
|
|
hsh::float3{-1.f, -1.f, 0.f}, {}, Rainbow[CurColor]};
|
|
VertData[1] =
|
|
MyNS::MyFormat{hsh::float3{1.f, -1.f, 0.f},
|
|
{},
|
|
Rainbow[(CurColor + 1) % Rainbow.size()]};
|
|
VertData[2] =
|
|
MyNS::MyFormat{hsh::float3{1.f, 1.f, 0.f},
|
|
{},
|
|
Rainbow[(CurColor + 2) % Rainbow.size()]};
|
|
});
|
|
// CurColor = (CurColor + 1) % Rainbow.size();
|
|
MyNS::BindPipeline(PipelineBind, UFifoBinding, VFifoBinding).draw(0, 3);
|
|
|
|
RenderTexture.resolve_surface(Surface.get());
|
|
}
|
|
});
|
|
return true;
|
|
});
|
|
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
|
|
int main(int argc, char **argv) {
|
|
MyNS::BuildPipelineTemplated(false, MyNS::AM_Alpha);
|
|
MyNS::BuildPipelineTemplated(false, MyNS::AM_NoAlpha);
|
|
MyNS::BuildPipelineTemplated(true, MyNS::AM_NoAlpha);
|
|
|
|
ModelInfo ModInfo = CreateModelInfo();
|
|
MaterialInfo MatInfo = CreateMaterialInfo();
|
|
ModelResources ModRes = CreateModelResources();
|
|
BindDrawModel(ModInfo, MatInfo, PT_Normal, ModRes);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|