mirror of https://github.com/auygun/kaliber.git
Compare commits
2 Commits
64715595d3
...
2ac1520c9b
Author | SHA1 | Date |
---|---|---|
Attila Uygun | 2ac1520c9b | |
Attila Uygun | 8503c549d7 |
|
@ -23,6 +23,17 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
"preLaunchTask": "Build project",
|
"preLaunchTask": "Build project",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "C/C++: cl.exe build and debug active file",
|
||||||
|
"type": "cppvsdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}\\out\\debug\\demo.exe",
|
||||||
|
"args": [],
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"cwd": "${workspaceFolder}\\out\\debug",
|
||||||
|
"environment": [],
|
||||||
|
"externalConsole": false,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -173,7 +173,9 @@ config("warnings") {
|
||||||
# Disable the following warnings:
|
# Disable the following warnings:
|
||||||
"/wd4061", # enumerator in switch with a default case not explicitly
|
"/wd4061", # enumerator in switch with a default case not explicitly
|
||||||
# handled by a case label.
|
# handled by a case label.
|
||||||
"/wd4100", # unreferenced format parameter.
|
"/wd4100", # nonstandard extension used: nameless struct/union.
|
||||||
|
"/wd4201", # enumerator in switch with a default case not explicitly
|
||||||
|
# handled by a case label.
|
||||||
"/wd4324", # structure was padded due to alignment specifier
|
"/wd4324", # structure was padded due to alignment specifier
|
||||||
"/wd4371", # layout of class may have changed from a previous version of
|
"/wd4371", # layout of class may have changed from a previous version of
|
||||||
# the compiler due to better packing of member
|
# the compiler due to better packing of member
|
||||||
|
@ -185,6 +187,10 @@ config("warnings") {
|
||||||
# switch specified.
|
# switch specified.
|
||||||
|
|
||||||
# TODO: Not sure how I feel about these conversion warnings.
|
# TODO: Not sure how I feel about these conversion warnings.
|
||||||
|
"/Wv:18", # tmp
|
||||||
|
"/wd4191", # tmp:'type cast': unsafe conversion
|
||||||
|
"/wd4242", # tmp:conversion from 'int' to 'uint8_t'
|
||||||
|
"/wd4245", # tmp:conversion from 'int' to 'const unsigned int'
|
||||||
"/wd4244", # conversion, possible loss of data. 'int' to 'float'
|
"/wd4244", # conversion, possible loss of data. 'int' to 'float'
|
||||||
"/wd4267", # conversion, possible loss of data.
|
"/wd4267", # conversion, possible loss of data.
|
||||||
"/wd4305", # truncation from 'double' to 'float'.
|
"/wd4305", # truncation from 'double' to 'float'.
|
||||||
|
@ -232,6 +238,7 @@ config("warnings") {
|
||||||
defines = [
|
defines = [
|
||||||
"_CRT_NONSTDC_NO_DEPRECATE",
|
"_CRT_NONSTDC_NO_DEPRECATE",
|
||||||
"_SILENCE_CXX17_STRSTREAM_DEPRECATION_WARNING",
|
"_SILENCE_CXX17_STRSTREAM_DEPRECATION_WARNING",
|
||||||
|
"_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS",
|
||||||
]
|
]
|
||||||
} else if (is_mac) {
|
} else if (is_mac) {
|
||||||
cflags = [
|
cflags = [
|
||||||
|
|
|
@ -49,6 +49,7 @@ project(kaliber)
|
||||||
add_library(kaliber SHARED
|
add_library(kaliber SHARED
|
||||||
../../../src/base/collusion_test.cc
|
../../../src/base/collusion_test.cc
|
||||||
../../../src/base/log.cc
|
../../../src/base/log.cc
|
||||||
|
../../../src/base/mem.cc
|
||||||
../../../src/base/task_runner.cc
|
../../../src/base/task_runner.cc
|
||||||
../../../src/base/thread_pool.cc
|
../../../src/base/thread_pool.cc
|
||||||
../../../src/demo/credits.cc
|
../../../src/demo/credits.cc
|
||||||
|
@ -136,7 +137,6 @@ add_library(kaliber SHARED
|
||||||
../../../src/third_party/minizip/ioapi.c
|
../../../src/third_party/minizip/ioapi.c
|
||||||
../../../src/third_party/minizip/unzip.c
|
../../../src/third_party/minizip/unzip.c
|
||||||
../../../src/third_party/spirv-reflect/spirv_reflect.c
|
../../../src/third_party/spirv-reflect/spirv_reflect.c
|
||||||
../../../src/third_party/stb/stb_image.c
|
|
||||||
../../../src/third_party/texture_compressor/dxt_encoder_internals.cc
|
../../../src/third_party/texture_compressor/dxt_encoder_internals.cc
|
||||||
../../../src/third_party/texture_compressor/dxt_encoder.cc
|
../../../src/third_party/texture_compressor/dxt_encoder.cc
|
||||||
../../../src/third_party/texture_compressor/texture_compressor_etc1.cc
|
../../../src/third_party/texture_compressor/texture_compressor_etc1.cc
|
||||||
|
|
|
@ -8,6 +8,7 @@ source_set("base") {
|
||||||
"interpolation.h",
|
"interpolation.h",
|
||||||
"log.cc",
|
"log.cc",
|
||||||
"log.h",
|
"log.h",
|
||||||
|
"mem.cc",
|
||||||
"mem.h",
|
"mem.h",
|
||||||
"misc.h",
|
"misc.h",
|
||||||
"random.h",
|
"random.h",
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
#include "base/mem.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
#include <malloc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
|
||||||
|
void* AlignedAlloc(size_t size, size_t alignment) {
|
||||||
|
DCHECK(size > 0U);
|
||||||
|
DCHECK(IsPow2(alignment));
|
||||||
|
DCHECK((alignment % sizeof(void*)) == 0U);
|
||||||
|
|
||||||
|
void* ptr = nullptr;
|
||||||
|
#if defined(_WIN32)
|
||||||
|
ptr = _aligned_malloc(size, alignment);
|
||||||
|
#elif defined(__ANDROID__)
|
||||||
|
ptr = memalign(alignment, size);
|
||||||
|
#else
|
||||||
|
int ret = posix_memalign(&ptr, alignment, size);
|
||||||
|
if (ret != 0) {
|
||||||
|
DLOG(0) << "posix_memalign() returned with error " << ret;
|
||||||
|
ptr = nullptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Aligned allocations may fail for non-memory related reasons.
|
||||||
|
CHECK(ptr) << "Aligned allocation failed. "
|
||||||
|
<< "size=" << size << ", alignment=" << alignment;
|
||||||
|
DCHECK(IsAligned(ptr, alignment));
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* AlignedRealloc(void* ptr,
|
||||||
|
size_t old_size,
|
||||||
|
size_t new_size,
|
||||||
|
size_t alignment) {
|
||||||
|
auto* new_ptr = AlignedAlloc(new_size, alignment);
|
||||||
|
memmove(new_ptr, ptr, old_size);
|
||||||
|
AlignedFree(ptr);
|
||||||
|
return new_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace base
|
|
@ -1,26 +1,33 @@
|
||||||
#ifndef BASE_MEM_H
|
#ifndef BASE_MEM_H
|
||||||
#define BASE_MEM_H
|
#define BASE_MEM_H
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
#if defined(_WIN32)
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
|
#else
|
||||||
|
#include <stdlib.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "base/log.h"
|
#include "base/log.h"
|
||||||
|
#include "base/misc.h"
|
||||||
#define ALIGN_MEM(alignment) __attribute__((aligned(alignment)))
|
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
|
|
||||||
|
inline void AlignedFree(void* mem) {
|
||||||
|
#if defined(_WIN32)
|
||||||
|
_aligned_free(mem);
|
||||||
|
#else
|
||||||
|
free(mem);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
struct ScopedAlignedFree {
|
struct ScopedAlignedFree {
|
||||||
inline void operator()(void* x) const {
|
inline void operator()(void* x) const { AlignedFree(x); }
|
||||||
if (x)
|
|
||||||
free(x);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
@ -28,27 +35,16 @@ struct ScopedAlignedFree {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using AlignedMemPtr = std::unique_ptr<T, internal::ScopedAlignedFree>;
|
using AlignedMemPtr = std::unique_ptr<T, internal::ScopedAlignedFree>;
|
||||||
|
|
||||||
template <int kAlignment>
|
void* AlignedAlloc(size_t size, size_t alignment);
|
||||||
inline void* AlignedAlloc(size_t size) {
|
|
||||||
void* ptr = NULL;
|
|
||||||
#if defined(__ANDROID__)
|
|
||||||
ptr = memalign(kAlignment, size);
|
|
||||||
#else
|
|
||||||
if (posix_memalign(&ptr, kAlignment, size))
|
|
||||||
ptr = NULL;
|
|
||||||
#endif
|
|
||||||
DCHECK(ptr);
|
|
||||||
// DCHECK(((unsigned)ptr & (kAlignment - 1)) == 0);
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void AlignedFree(void* mem) {
|
void* AlignedRealloc(void* ptr,
|
||||||
free(mem);
|
size_t old_size,
|
||||||
}
|
size_t new_size,
|
||||||
|
size_t alignment);
|
||||||
|
|
||||||
template <int kAlignment>
|
inline bool IsAligned(const void* val, size_t alignment) {
|
||||||
inline bool IsAligned(void* ptr) {
|
DCHECK(IsPow2(alignment)) << alignment << " is not a power of 2";
|
||||||
return (reinterpret_cast<uintptr_t>(ptr) & (kAlignment - 1)) == 0U;
|
return (reinterpret_cast<uintptr_t>(val) & (alignment - 1)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
|
|
@ -1247,8 +1247,8 @@ class Matrix4 {
|
||||||
T fov_aspect,
|
T fov_aspect,
|
||||||
T width,
|
T width,
|
||||||
T height,
|
T height,
|
||||||
T near,
|
T near_,
|
||||||
T far) {
|
T far_) {
|
||||||
// Calc x and y scale from FOV.
|
// Calc x and y scale from FOV.
|
||||||
T scale =
|
T scale =
|
||||||
T(2.0) /
|
T(2.0) /
|
||||||
|
@ -1257,8 +1257,8 @@ class Matrix4 {
|
||||||
T x_scale = y_scale / (width / height);
|
T x_scale = y_scale / (width / height);
|
||||||
_M_SET_ROW(0, x_scale / T(2.0), 0, 0, 0);
|
_M_SET_ROW(0, x_scale / T(2.0), 0, 0, 0);
|
||||||
_M_SET_ROW(1, 0, (-y_scale) / T(2.0), 0, 0);
|
_M_SET_ROW(1, 0, (-y_scale) / T(2.0), 0, 0);
|
||||||
_M_SET_ROW(2, 0, 0, far / (far - near), 1);
|
_M_SET_ROW(2, 0, 0, far_ / (far_ - near_), 1);
|
||||||
_M_SET_ROW(3, 0, 0, -near * far / (far - near), 0);
|
_M_SET_ROW(3, 0, 0, -near_ * far_ / (far_ - near_), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreateTranslation(const Vector3<T>& t) {
|
void CreateTranslation(const Vector3<T>& t) {
|
||||||
|
|
|
@ -18,7 +18,7 @@ executable("demo") {
|
||||||
]
|
]
|
||||||
|
|
||||||
deps = [
|
deps = [
|
||||||
"//assets",
|
# "//assets",
|
||||||
"//src/base",
|
"//src/base",
|
||||||
"//src/engine",
|
"//src/engine",
|
||||||
]
|
]
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "base/interpolation.h"
|
#include "base/interpolation.h"
|
||||||
#include "base/log.h"
|
#include "base/log.h"
|
||||||
|
#include "base/vecmath.h"
|
||||||
#include "engine/asset/font.h"
|
#include "engine/asset/font.h"
|
||||||
#include "engine/engine.h"
|
#include "engine/engine.h"
|
||||||
#include "engine/input_event.h"
|
#include "engine/input_event.h"
|
||||||
|
|
|
@ -80,6 +80,23 @@ source_set("engine") {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target_os == "win") {
|
||||||
|
sources += [
|
||||||
|
"audio/audio_sink_null.h",
|
||||||
|
"platform/asset_file_linux.cc",
|
||||||
|
"platform/platform_win.cc",
|
||||||
|
"renderer/opengl/renderer_opengl_win.cc",
|
||||||
|
"renderer/vulkan/renderer_vulkan_win.cc",
|
||||||
|
"renderer/vulkan/vulkan_context_win.cc",
|
||||||
|
]
|
||||||
|
|
||||||
|
libs = [
|
||||||
|
# "gdi32.lib", # Graphics
|
||||||
|
"user32.lib", # Win32 API core functionality.
|
||||||
|
# "shell32.lib", # Drag and Drop
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
deps = [
|
deps = [
|
||||||
"//assets/engine",
|
"//assets/engine",
|
||||||
"//src/base",
|
"//src/base",
|
||||||
|
|
|
@ -5,13 +5,19 @@
|
||||||
|
|
||||||
#include "base/interpolation.h"
|
#include "base/interpolation.h"
|
||||||
#include "base/log.h"
|
#include "base/log.h"
|
||||||
|
#include "base/mem.h"
|
||||||
#include "base/misc.h"
|
#include "base/misc.h"
|
||||||
#include "engine/engine.h"
|
#include "engine/engine.h"
|
||||||
#include "engine/platform/asset_file.h"
|
#include "engine/platform/asset_file.h"
|
||||||
#include "third_party/texture_compressor/texture_compressor.h"
|
#include "third_party/texture_compressor/texture_compressor.h"
|
||||||
|
|
||||||
// This 3rd party library is written in C and uses malloc, which means that we
|
// Use aligned memory for SIMD in texture compressor.
|
||||||
// have to do the same.
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#define STBI_NO_STDIO
|
||||||
|
#define STBI_MALLOC(sz) base::AlignedAlloc(sz, 16)
|
||||||
|
#define STBI_REALLOC_SIZED(p, oldsz, newsz) \
|
||||||
|
base::AlignedRealloc(p, oldsz, newsz, 16)
|
||||||
|
#define STBI_FREE(p) base::AlignedFree(p)
|
||||||
#include "third_party/stb/stb_image.h"
|
#include "third_party/stb/stb_image.h"
|
||||||
|
|
||||||
using namespace base;
|
using namespace base;
|
||||||
|
@ -77,7 +83,7 @@ bool Image::Create(int w, int h) {
|
||||||
width_ = w;
|
width_ = w;
|
||||||
height_ = h;
|
height_ = h;
|
||||||
|
|
||||||
buffer_.reset((uint8_t*)AlignedAlloc<16>(w * h * 4 * sizeof(uint8_t)));
|
buffer_.reset((uint8_t*)AlignedAlloc(w * h * 4 * sizeof(uint8_t), 16));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -85,7 +91,7 @@ bool Image::Create(int w, int h) {
|
||||||
void Image::Copy(const Image& other) {
|
void Image::Copy(const Image& other) {
|
||||||
if (other.buffer_) {
|
if (other.buffer_) {
|
||||||
int size = other.GetSize();
|
int size = other.GetSize();
|
||||||
buffer_.reset((uint8_t*)AlignedAlloc<16>(size));
|
buffer_.reset((uint8_t*)AlignedAlloc(size, 16));
|
||||||
memcpy(buffer_.get(), other.buffer_.get(), size);
|
memcpy(buffer_.get(), other.buffer_.get(), size);
|
||||||
}
|
}
|
||||||
width_ = other.width_;
|
width_ = other.width_;
|
||||||
|
@ -101,7 +107,7 @@ bool Image::CreateMip(const Image& other) {
|
||||||
width_ = std::max(other.width_ >> 1, 1);
|
width_ = std::max(other.width_ >> 1, 1);
|
||||||
height_ = std::max(other.height_ >> 1, 1);
|
height_ = std::max(other.height_ >> 1, 1);
|
||||||
format_ = kRGBA32;
|
format_ = kRGBA32;
|
||||||
buffer_.reset((uint8_t*)AlignedAlloc<16>(GetSize()));
|
buffer_.reset((uint8_t*)AlignedAlloc(GetSize(), 16));
|
||||||
|
|
||||||
// If the width isn't perfectly divisable with two, then we end up skewing
|
// If the width isn't perfectly divisable with two, then we end up skewing
|
||||||
// the image because the source offset isn't updated properly.
|
// the image because the source offset isn't updated properly.
|
||||||
|
@ -158,7 +164,7 @@ bool Image::Load(const std::string& file_name) {
|
||||||
// LOG(0)("Converting image from 1 to 4 channels.\n");
|
// LOG(0)("Converting image from 1 to 4 channels.\n");
|
||||||
// Assume it's an intensity, duplicate it to RGB and fill A with opaque.
|
// Assume it's an intensity, duplicate it to RGB and fill A with opaque.
|
||||||
converted_buffer =
|
converted_buffer =
|
||||||
(uint8_t*)AlignedAlloc<16>(w * h * 4 * sizeof(uint8_t));
|
(uint8_t*)AlignedAlloc(w * h * 4 * sizeof(uint8_t), 16);
|
||||||
for (int i = 0; i < w * h; ++i) {
|
for (int i = 0; i < w * h; ++i) {
|
||||||
converted_buffer[i * 4 + 0] = buffer_[i];
|
converted_buffer[i * 4 + 0] = buffer_[i];
|
||||||
converted_buffer[i * 4 + 1] = buffer_[i];
|
converted_buffer[i * 4 + 1] = buffer_[i];
|
||||||
|
@ -171,7 +177,7 @@ bool Image::Load(const std::string& file_name) {
|
||||||
// LOG(0)("Converting image from 3 to 4 channels.\n");
|
// LOG(0)("Converting image from 3 to 4 channels.\n");
|
||||||
// Add an opaque channel.
|
// Add an opaque channel.
|
||||||
converted_buffer =
|
converted_buffer =
|
||||||
(uint8_t*)AlignedAlloc<16>(w * h * 4 * sizeof(uint8_t));
|
(uint8_t*)AlignedAlloc(w * h * 4 * sizeof(uint8_t), 16);
|
||||||
for (int i = 0; i < w * h; ++i) {
|
for (int i = 0; i < w * h; ++i) {
|
||||||
converted_buffer[i * 4 + 0] = buffer_[i * 3 + 0];
|
converted_buffer[i * 4 + 0] = buffer_[i * 3 + 0];
|
||||||
converted_buffer[i * 4 + 1] = buffer_[i * 3 + 1];
|
converted_buffer[i * 4 + 1] = buffer_[i * 3 + 1];
|
||||||
|
@ -238,7 +244,7 @@ void Image::ConvertToPow2() {
|
||||||
<< new_width << ", " << new_height << ")";
|
<< new_width << ", " << new_height << ")";
|
||||||
|
|
||||||
int bigger_size = new_width * new_height * 4 * sizeof(uint8_t);
|
int bigger_size = new_width * new_height * 4 * sizeof(uint8_t);
|
||||||
uint8_t* bigger_buffer = (uint8_t*)AlignedAlloc<16>(bigger_size);
|
uint8_t* bigger_buffer = (uint8_t*)AlignedAlloc(bigger_size, 16);
|
||||||
|
|
||||||
// Fill it with black.
|
// Fill it with black.
|
||||||
memset(bigger_buffer, 0, bigger_size);
|
memset(bigger_buffer, 0, bigger_size);
|
||||||
|
@ -296,7 +302,7 @@ bool Image::Compress() {
|
||||||
|
|
||||||
unsigned compressedSize = GetSize();
|
unsigned compressedSize = GetSize();
|
||||||
uint8_t* compressedBuffer =
|
uint8_t* compressedBuffer =
|
||||||
(uint8_t*)AlignedAlloc<16>(compressedSize * sizeof(uint8_t));
|
(uint8_t*)AlignedAlloc(compressedSize * sizeof(uint8_t), 16);
|
||||||
|
|
||||||
const uint8_t* src = buffer_.get();
|
const uint8_t* src = buffer_.get();
|
||||||
uint8_t* dst = compressedBuffer;
|
uint8_t* dst = compressedBuffer;
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
#include "engine/audio/audio_sink_oboe.h"
|
#include "engine/audio/audio_sink_oboe.h"
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
#include "engine/audio/audio_sink_alsa.h"
|
#include "engine/audio/audio_sink_alsa.h"
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
#include "engine/audio/audio_sink_null.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using namespace base;
|
using namespace base;
|
||||||
|
@ -23,6 +25,8 @@ AudioMixer::AudioMixer()
|
||||||
audio_sink_{std::make_unique<AudioSinkOboe>(this)} {
|
audio_sink_{std::make_unique<AudioSinkOboe>(this)} {
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
audio_sink_{std::make_unique<AudioSinkAlsa>(this)} {
|
audio_sink_{std::make_unique<AudioSinkAlsa>(this)} {
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
audio_sink_{std::make_unique<AudioSinkNull>()} {
|
||||||
#endif
|
#endif
|
||||||
bool res = audio_sink_->Initialize();
|
bool res = audio_sink_->Initialize();
|
||||||
CHECK(res) << "Failed to initialize audio sink.";
|
CHECK(res) << "Failed to initialize audio sink.";
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef ENGINE_AUDIO_AUDIO_SINK_NULL_H
|
||||||
|
#define ENGINE_AUDIO_AUDIO_SINK_NULL_H
|
||||||
|
|
||||||
|
#include "engine/audio/audio_sink.h"
|
||||||
|
|
||||||
|
namespace eng {
|
||||||
|
|
||||||
|
class AudioSinkNull final : public AudioSink {
|
||||||
|
public:
|
||||||
|
AudioSinkNull() = default;
|
||||||
|
~AudioSinkNull() final = default;
|
||||||
|
|
||||||
|
bool Initialize() final { return true; }
|
||||||
|
|
||||||
|
void Suspend() final {}
|
||||||
|
void Resume() final {}
|
||||||
|
|
||||||
|
size_t GetHardwareSampleRate() final { return 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace eng
|
||||||
|
|
||||||
|
#endif // ENGINE_AUDIO_AUDIO_SINK_NULL_H
|
|
@ -192,13 +192,13 @@ SincResampler::SincResampler(double io_sample_rate_ratio, int request_frames)
|
||||||
input_buffer_size_(request_frames_ + kernel_size_),
|
input_buffer_size_(request_frames_ + kernel_size_),
|
||||||
// Create input buffers with a 32-byte alignment for SIMD optimizations.
|
// Create input buffers with a 32-byte alignment for SIMD optimizations.
|
||||||
kernel_storage_(static_cast<float*>(
|
kernel_storage_(static_cast<float*>(
|
||||||
base::AlignedAlloc<32>(sizeof(float) * kernel_storage_size_))),
|
base::AlignedAlloc(sizeof(float) * kernel_storage_size_, 32))),
|
||||||
kernel_pre_sinc_storage_(static_cast<float*>(
|
kernel_pre_sinc_storage_(static_cast<float*>(
|
||||||
base::AlignedAlloc<32>(sizeof(float) * kernel_storage_size_))),
|
base::AlignedAlloc(sizeof(float) * kernel_storage_size_, 32))),
|
||||||
kernel_window_storage_(static_cast<float*>(
|
kernel_window_storage_(static_cast<float*>(
|
||||||
base::AlignedAlloc<32>(sizeof(float) * kernel_storage_size_))),
|
base::AlignedAlloc(sizeof(float) * kernel_storage_size_, 32))),
|
||||||
input_buffer_(static_cast<float*>(
|
input_buffer_(static_cast<float*>(
|
||||||
base::AlignedAlloc<32>(sizeof(float) * input_buffer_size_))),
|
base::AlignedAlloc(sizeof(float) * input_buffer_size_, 32))),
|
||||||
r1_(input_buffer_.get()),
|
r1_(input_buffer_.get()),
|
||||||
r2_(input_buffer_.get() + kernel_size_ / 2) {
|
r2_(input_buffer_.get() + kernel_size_ / 2) {
|
||||||
CHECK(request_frames > kernel_size_ * 3 / 2)
|
CHECK(request_frames > kernel_size_ * 3 / 2)
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#include "third_party/minizip/unzip.h"
|
#include "third_party/minizip/unzip.h"
|
||||||
#elif defined(__linux__)
|
#else
|
||||||
#include "base/file.h"
|
#include "base/file.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ class AssetFile {
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
unzFile archive_ = 0;
|
unzFile archive_ = 0;
|
||||||
size_t uncompressed_size_ = 0;
|
size_t uncompressed_size_ = 0;
|
||||||
#elif defined(__linux)
|
#else
|
||||||
base::ScopedFILE file_;
|
base::ScopedFILE file_;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,6 +16,13 @@ struct ANativeWindow;
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#include <X11/Xutil.h>
|
#include <X11/Xutil.h>
|
||||||
|
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
|
||||||
|
// #include <WinSDKVer.h>
|
||||||
|
// #define _WIN32_WINNT 0x0601
|
||||||
|
// #include <SDKDDKVer.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace eng {
|
namespace eng {
|
||||||
|
@ -28,6 +35,8 @@ class Platform {
|
||||||
Platform(android_app* app);
|
Platform(android_app* app);
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
Platform();
|
Platform();
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
Platform(HINSTANCE hInstance, int nCmdShow);
|
||||||
#endif
|
#endif
|
||||||
~Platform();
|
~Platform();
|
||||||
|
|
||||||
|
@ -62,6 +71,9 @@ class Platform {
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
Display* GetDisplay();
|
Display* GetDisplay();
|
||||||
Window GetWindow();
|
Window GetWindow();
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
HINSTANCE GetInstance();
|
||||||
|
HWND GetWindow();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -111,6 +123,15 @@ class Platform {
|
||||||
|
|
||||||
XVisualInfo* GetXVisualInfo(Display* display);
|
XVisualInfo* GetXVisualInfo(Display* display);
|
||||||
|
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
|
||||||
|
// Global Variables:
|
||||||
|
HINSTANCE hInst; // current instance
|
||||||
|
HWND hWnd;
|
||||||
|
|
||||||
|
// Forward declarations of functions included in this code module:
|
||||||
|
static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Platform(const Platform&) = delete;
|
Platform(const Platform&) = delete;
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
#include "engine/platform/platform.h"
|
||||||
|
|
||||||
|
// #include <limits.h>
|
||||||
|
// #include <stdio.h>
|
||||||
|
// #include <cstring>
|
||||||
|
// #include <memory>
|
||||||
|
//
|
||||||
|
#include "base/log.h"
|
||||||
|
// #include "base/vecmath.h"
|
||||||
|
// #include "engine/input_event.h"
|
||||||
|
// #include "engine/platform/platform_observer.h"
|
||||||
|
|
||||||
|
using namespace base;
|
||||||
|
|
||||||
|
namespace eng {
|
||||||
|
|
||||||
|
void KaliberMain(Platform* platform);
|
||||||
|
|
||||||
|
Platform::Platform(HINSTANCE hInstance, int nCmdShow) : hInst(hInstance) {
|
||||||
|
LOG(0) << "Initializing platform.";
|
||||||
|
|
||||||
|
root_path_ = "./";
|
||||||
|
data_path_ = "./";
|
||||||
|
shared_data_path_ = "./";
|
||||||
|
|
||||||
|
LOG(0) << "Root path: " << root_path_.c_str();
|
||||||
|
LOG(0) << "Data path: " << data_path_.c_str();
|
||||||
|
LOG(0) << "Shared data path: " << shared_data_path_.c_str();
|
||||||
|
|
||||||
|
WNDCLASSEXW wcex;
|
||||||
|
wcex.cbSize = sizeof(WNDCLASSEX);
|
||||||
|
wcex.style = CS_HREDRAW | CS_VREDRAW;
|
||||||
|
wcex.lpfnWndProc = WndProc;
|
||||||
|
wcex.cbClsExtra = 0;
|
||||||
|
wcex.cbWndExtra = 0;
|
||||||
|
wcex.hInstance = hInstance;
|
||||||
|
wcex.hIcon =
|
||||||
|
nullptr; // LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSPROJECT1));
|
||||||
|
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||||
|
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
||||||
|
wcex.lpszMenuName = nullptr; // MAKEINTRESOURCEW(IDC_WINDOWSPROJECT1);
|
||||||
|
wcex.lpszClassName = L"szWindowClass";
|
||||||
|
wcex.hIconSm =
|
||||||
|
nullptr; // LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
|
||||||
|
RegisterClassExW(&wcex);
|
||||||
|
|
||||||
|
hWnd = CreateWindowW(L"szWindowClass", L"kaliber", WS_OVERLAPPEDWINDOW,
|
||||||
|
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr,
|
||||||
|
hInstance, nullptr);
|
||||||
|
CHECK(hWnd);
|
||||||
|
|
||||||
|
ShowWindow(hWnd, nCmdShow);
|
||||||
|
UpdateWindow(hWnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform::~Platform() {
|
||||||
|
LOG(0) << "Shutting down platform.";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Platform::Update() {}
|
||||||
|
|
||||||
|
void Platform::Exit() {
|
||||||
|
should_exit_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Platform::Vibrate(int duration) {}
|
||||||
|
|
||||||
|
void Platform::ShowInterstitialAd() {}
|
||||||
|
|
||||||
|
void Platform::ShareFile(const std::string& file_name) {}
|
||||||
|
|
||||||
|
void Platform::SetKeepScreenOn(bool keep_screen_on) {}
|
||||||
|
|
||||||
|
HINSTANCE Platform::GetInstance() {
|
||||||
|
return hInst;
|
||||||
|
}
|
||||||
|
|
||||||
|
HWND Platform::GetWindow() {
|
||||||
|
return hWnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT CALLBACK Platform::WndProc(HWND hWnd,
|
||||||
|
UINT message,
|
||||||
|
WPARAM wParam,
|
||||||
|
LPARAM lParam) {
|
||||||
|
// switch (message)
|
||||||
|
// {
|
||||||
|
// case WM_COMMAND:
|
||||||
|
// {
|
||||||
|
// int wmId = LOWORD(wParam);
|
||||||
|
// // Parse the menu selections:
|
||||||
|
// switch (wmId)
|
||||||
|
// {
|
||||||
|
// case IDM_ABOUT:
|
||||||
|
// DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
|
||||||
|
// break;
|
||||||
|
// case IDM_EXIT:
|
||||||
|
// DestroyWindow(hWnd);
|
||||||
|
// break;
|
||||||
|
// default:
|
||||||
|
// return DefWindowProc(hWnd, message, wParam, lParam);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// break;
|
||||||
|
// case WM_PAINT:
|
||||||
|
// {
|
||||||
|
// PAINTSTRUCT ps;
|
||||||
|
// HDC hdc = BeginPaint(hWnd, &ps);
|
||||||
|
// // TODO: Add any drawing code that uses hdc here...
|
||||||
|
// EndPaint(hWnd, &ps);
|
||||||
|
// }
|
||||||
|
// break;
|
||||||
|
// case WM_DESTROY:
|
||||||
|
// PostQuitMessage(0);
|
||||||
|
// break;
|
||||||
|
// default:
|
||||||
|
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||||
|
// }
|
||||||
|
// return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace eng
|
||||||
|
|
||||||
|
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
|
||||||
|
_In_opt_ HINSTANCE hPrevInstance,
|
||||||
|
_In_ LPWSTR lpCmdLine,
|
||||||
|
_In_ int nCmdShow) {
|
||||||
|
eng::Platform platform(hInstance, nCmdShow);
|
||||||
|
eng::KaliberMain(&platform);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -17,6 +17,16 @@
|
||||||
#define GL_ETC1_RGB8_OES 0x8D64
|
#define GL_ETC1_RGB8_OES 0x8D64
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
#define GLEW_STATIC
|
||||||
|
#include "third_party/glew/glew.h"
|
||||||
|
#include "third_party/glew/wglew.h"
|
||||||
|
|
||||||
|
// Define the missing format for the etc1
|
||||||
|
#ifndef GL_ETC1_RGB8_OES
|
||||||
|
#define GL_ETC1_RGB8_OES 0x8D64
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // ENGINE_RENDERER_OPENGL_OPENGL_H
|
#endif // ENGINE_RENDERER_OPENGL_OPENGL_H
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
#include "engine/renderer/opengl/renderer_opengl.h"
|
||||||
|
|
||||||
|
#include "base/log.h"
|
||||||
|
#include "engine/platform/platform.h"
|
||||||
|
|
||||||
|
namespace eng {
|
||||||
|
|
||||||
|
bool RendererOpenGL::Initialize(Platform* platform) {
|
||||||
|
LOG(0) << "Initializing renderer.";
|
||||||
|
|
||||||
|
return InitCommon();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RendererOpenGL::OnDestroy() {}
|
||||||
|
|
||||||
|
void RendererOpenGL::Shutdown() {
|
||||||
|
LOG(0) << "Shutting down renderer.";
|
||||||
|
is_initialized_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RendererOpenGL::Present() {
|
||||||
|
active_shader_id_ = 0;
|
||||||
|
active_texture_id_ = 0;
|
||||||
|
fps_++;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace eng
|
|
@ -0,0 +1,29 @@
|
||||||
|
#include "engine/renderer/vulkan/renderer_vulkan.h"
|
||||||
|
|
||||||
|
#include "base/log.h"
|
||||||
|
#include "engine/platform/platform.h"
|
||||||
|
|
||||||
|
namespace eng {
|
||||||
|
|
||||||
|
bool RendererVulkan::Initialize(Platform* platform) {
|
||||||
|
LOG(0) << "Initializing renderer.";
|
||||||
|
|
||||||
|
// XWindowAttributes xwa;
|
||||||
|
// XGetWindowAttributes(platform->GetInstance(), platform->GetWindow(), &xwa);
|
||||||
|
RECT rect;
|
||||||
|
GetClientRect(platform->GetWindow(), &rect);
|
||||||
|
|
||||||
|
if (!context_.Initialize()) {
|
||||||
|
LOG(0) << "Failed to initialize Vulkan context.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!context_.CreateSurface(platform->GetInstance(), platform->GetWindow(),
|
||||||
|
rect.right, rect.bottom)) {
|
||||||
|
LOG(0) << "Vulkan context failed to create window.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return InitializeInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace eng
|
|
@ -703,7 +703,7 @@ bool VulkanContext::InitializeQueues(VkSurfaceKHR surface) {
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
VkFormat desired_format = VK_FORMAT_R8G8B8A8_UNORM;
|
VkFormat desired_format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__) || defined(_WIN32)
|
||||||
VkFormat desired_format = VK_FORMAT_B8G8R8A8_UNORM;
|
VkFormat desired_format = VK_FORMAT_B8G8R8A8_UNORM;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,10 @@
|
||||||
struct ANativeWindow;
|
struct ANativeWindow;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// #if defined(_WIN32)
|
||||||
|
// #include <windows.h>
|
||||||
|
// #endif
|
||||||
|
|
||||||
namespace eng {
|
namespace eng {
|
||||||
|
|
||||||
// Adapted from godot engin.
|
// Adapted from godot engin.
|
||||||
|
@ -27,6 +31,8 @@ class VulkanContext {
|
||||||
bool CreateWindow(ANativeWindow* window, int width, int height);
|
bool CreateWindow(ANativeWindow* window, int width, int height);
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
bool CreateWindow(Display* display, ::Window window, int width, int height);
|
bool CreateWindow(Display* display, ::Window window, int width, int height);
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
bool CreateSurface(HINSTANCE hInstance, HWND hWnd, int width, int height);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void ResizeWindow(int width, int height);
|
void ResizeWindow(int width, int height);
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
#include "engine/renderer/vulkan/vulkan_context.h"
|
||||||
|
|
||||||
|
// #if defined(_WIN32)
|
||||||
|
// #include <windows.h>
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
#include "base/log.h"
|
||||||
|
|
||||||
|
namespace eng {
|
||||||
|
|
||||||
|
const char* VulkanContext::GetPlatformSurfaceExtension() const {
|
||||||
|
return VK_KHR_WIN32_SURFACE_EXTENSION_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VulkanContext::CreateSurface(HINSTANCE hInstance,
|
||||||
|
HWND hWnd,
|
||||||
|
int width,
|
||||||
|
int height) {
|
||||||
|
VkWin32SurfaceCreateInfoKHR create_info;
|
||||||
|
create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
|
||||||
|
create_info.pNext = nullptr;
|
||||||
|
create_info.flags = 0;
|
||||||
|
create_info.hinstance = hInstance;
|
||||||
|
create_info.hwnd = hWnd;
|
||||||
|
|
||||||
|
VkSurfaceKHR surface;
|
||||||
|
VkResult err =
|
||||||
|
vkCreateWin32SurfaceKHR(instance_, &create_info, nullptr, &surface);
|
||||||
|
if (err != VK_SUCCESS) {
|
||||||
|
LOG(0) << "vkCreateWin32SurfaceKHR failed with error "
|
||||||
|
<< std::to_string(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!queues_initialized_ && !InitializeQueues(surface))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
window_.surface = surface;
|
||||||
|
window_.width = width;
|
||||||
|
window_.height = height;
|
||||||
|
if (!UpdateSwapChain(&window_))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace eng
|
|
@ -7,6 +7,10 @@ config("third_party_config") {
|
||||||
if (target_os == "linux") {
|
if (target_os == "linux") {
|
||||||
defines = [ "VK_USE_PLATFORM_XLIB_KHR" ]
|
defines = [ "VK_USE_PLATFORM_XLIB_KHR" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target_os == "win") {
|
||||||
|
defines = [ "VK_USE_PLATFORM_WIN32_KHR" ]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
source_set("third_party") {
|
source_set("third_party") {
|
||||||
|
@ -53,13 +57,13 @@ source_set("third_party") {
|
||||||
"glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp",
|
"glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp",
|
||||||
"glslang/glslang/MachineIndependent/propagateNoContraction.cpp",
|
"glslang/glslang/MachineIndependent/propagateNoContraction.cpp",
|
||||||
"glslang/glslang/MachineIndependent/reflection.cpp",
|
"glslang/glslang/MachineIndependent/reflection.cpp",
|
||||||
"glslang/glslang/OSDependent/Unix/ossource.cpp",
|
# "glslang/glslang/OSDependent/Unix/ossource.cpp",
|
||||||
|
"glslang/glslang/OSDependent/Windows/ossource.cpp",
|
||||||
"jsoncpp/json.h",
|
"jsoncpp/json.h",
|
||||||
"jsoncpp/jsoncpp.cpp",
|
"jsoncpp/jsoncpp.cpp",
|
||||||
"minimp3/minimp3.h",
|
"minimp3/minimp3.h",
|
||||||
"minimp3/minimp3_ex.h",
|
"minimp3/minimp3_ex.h",
|
||||||
"spirv-reflect/spirv_reflect.c",
|
"spirv-reflect/spirv_reflect.c",
|
||||||
"stb/stb_image.c",
|
|
||||||
"stb/stb_image.h",
|
"stb/stb_image.h",
|
||||||
"stb/stb_truetype.h",
|
"stb/stb_truetype.h",
|
||||||
"texture_compressor/dxt_encoder.cc",
|
"texture_compressor/dxt_encoder.cc",
|
||||||
|
@ -85,6 +89,16 @@ source_set("third_party") {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target_os == "win") {
|
||||||
|
sources += [
|
||||||
|
"glew/glew.c",
|
||||||
|
"glew/glew.h",
|
||||||
|
"glew/wglew.h",
|
||||||
|
]
|
||||||
|
|
||||||
|
libs = [ "opengl32.lib" ]
|
||||||
|
}
|
||||||
|
|
||||||
configs -= [ "//build:warnings" ]
|
configs -= [ "//build:warnings" ]
|
||||||
|
|
||||||
deps = []
|
deps = []
|
||||||
|
|
|
@ -213,8 +213,8 @@ TIntermTyped* TIntermConstantUnion::fold(TOperator op, const TIntermTyped* right
|
||||||
case EbtInt64:
|
case EbtInt64:
|
||||||
if (rightUnionArray[i] == 0ll)
|
if (rightUnionArray[i] == 0ll)
|
||||||
newConstArray[i].setI64Const(0x7FFFFFFFFFFFFFFFll);
|
newConstArray[i].setI64Const(0x7FFFFFFFFFFFFFFFll);
|
||||||
else if (rightUnionArray[i].getI64Const() == -1 && leftUnionArray[i].getI64Const() == (long long)-0x8000000000000000ll)
|
else if (rightUnionArray[i].getI64Const() == -1 && leftUnionArray[i].getI64Const() == (long long)0x8000000000000000ll)
|
||||||
newConstArray[i].setI64Const((long long)-0x8000000000000000ll);
|
newConstArray[i].setI64Const((long long)0x8000000000000000ll);
|
||||||
else
|
else
|
||||||
newConstArray[i].setI64Const(leftUnionArray[i].getI64Const() / rightUnionArray[i].getI64Const());
|
newConstArray[i].setI64Const(leftUnionArray[i].getI64Const() / rightUnionArray[i].getI64Const());
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
|
||||||
#define STBI_NO_STDIO
|
|
||||||
#include "stb_image.h"
|
|
Loading…
Reference in New Issue