content and shader cooking stuff

This commit is contained in:
Peter Li 2025-04-13 14:12:48 -07:00
parent 653f61e8d9
commit f4381446db
202 changed files with 597 additions and 164878 deletions

View File

@ -8,6 +8,11 @@ nw_mod: *std.Build.Module,
spirvReflect: SpirvReflect.SpirvGenerator2,
gltf2ozz: ozz.GltfToOzz,
options: *std.Build.Step.Options,
cookShaders: bool,
// list of all shaders discovered under
// content/_shaders/def
reflectShaderPathList: [][]u8 = undefined,
const engineDepList = [_][]const u8{
"assets",
@ -16,9 +21,6 @@ const engineDepList = [_][]const u8{
"papyrus",
"platform",
"physics",
// "graphics",
// "ui",
// "vkImgui",
};
const BuildSystem = @This();
@ -50,24 +52,38 @@ pub fn init(b: *std.Build, opts: InitOptions) BuildSystem {
.spirvReflect = SpirvReflect.SpirvGenerator2.init(nwdep.builder, .{}),
.options = createGameOptions(b),
.gltf2ozz = ozz.GltfToOzz.init(nwdep.builder, .{}),
.cookShaders = b.option(bool, "cookShaders", "generates shaders and updates .json files before running the build. (needs to be done whenever shaders are updated, this just runs tools/scripts/cook-shaders.py)") orelse false,
};
b.installArtifact(self.spirvReflect.reflect);
b.installArtifact(self.gltf2ozz.exe);
const toolsInstall = b.addInstallArtifact(self.gltf2ozz.exe, .{
const exeList = [2]*std.Build.Step.Compile{ self.gltf2ozz.exe, self.spirvReflect.reflect };
const install_tools = b.step("tools", "installs tools needed to generate outputs for the engine");
for (exeList) |exe| {
const toolsInstall = b.addInstallArtifact(exe, .{
.dest_dir = .{ .override = .{ .custom = "tools" } },
});
install_tools.dependOn(&toolsInstall.step);
}
{
const runArtifact = b.addRunArtifact(self.gltf2ozz.exe);
if (b.args) |args| {
runArtifact.addArgs(args);
}
const run_exe = b.step("gltf2ozz", "runs the gltf animation converter.");
run_exe.dependOn(&runArtifact.step);
}
const install_tools = b.step("tools", "installs tools needed to run the engine");
install_tools.dependOn(&toolsInstall.step);
{
const runArtifact = b.addRunArtifact(self.spirvReflect.reflect);
if (b.args) |args| {
runArtifact.addArgs(args);
}
const run_exe = b.step("spv-reflect", "runs the gltf animation converter.");
run_exe.dependOn(&runArtifact.step);
}
return self;
}
@ -110,6 +126,8 @@ pub fn addProgram(self: *BuildSystem, opts: AddProgramOptions) *std.Build.Step.C
mod.addImport("Backlog", self.nw_mod);
exe.root_module.addOptions("BacklogOptions", self.options);
if (self.cookShaders) {}
// I want to generate definitions from the spirv-reflect-tool during pre-build.
//
// shaders will now be part of content, not code. However. .zig code definitions will be generated
@ -203,6 +221,7 @@ pub fn createGameOptions(b: *std.Build) *std.Build.Step.Options {
"use_renderthread",
true,
);
return opts;
}

View File

@ -8,11 +8,6 @@
.papyrus = .{ .path = "engine/papyrus" },
.physics = .{ .path = "engine/physics" },
.platform = .{ .path = "engine/platform" },
// .graphics = .{ .path = "engine/graphics" },
// .ui = .{ .path = "engine/ui" },
// .vkImgui = .{ .path = "engine/vkImgui" },
.SpirvReflect = .{ .path = "lib/spirv-reflect-zig" },
.ozz = .{ .path = "lib/ozz" },
},

1
content.txt Normal file
View File

@ -0,0 +1 @@
projects/content

View File

@ -1,71 +0,0 @@
const std = @import("std");
const SpirvReflect = @import("SpirvReflect");
const dependencyList = [_][]const u8{
"vulkan",
"vma",
"glfw3",
"core",
"assets",
"platform",
"objLoader",
"ozz",
};
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const mod = b.addModule("graphics", .{
.target = target,
.optimize = optimize,
.link_libc = true,
.root_source_file = b.path("src/graphics.zig"),
});
mod.addAnonymousImport("texture_sample.png", .{ .root_source_file = b.path("defaults/texture_sample.png") });
const options = b.addOptions();
options.addOption(bool, "force_mailbox", b.option(bool, "force_mailbox", "forces mailbox mode for present mode. unlocks framerate to irresponsible levels") orelse false);
mod.addOptions("game_build_opts", options);
for (dependencyList) |depName| {
const dep = b.dependency(depName, .{ .target = target, .optimize = optimize });
mod.addImport(depName, dep.module(depName));
if (std.mem.eql(u8, depName, "ozz")) {
mod.linkLibrary(dep.artifact("ozz_cpp"));
}
}
const spirvGen = SpirvReflect.SpirvGenerator2.init(b, .{ .optimize = optimize });
spirvGen.addShader(mod, b.path("shaders/triangle_mesh.vert"), "triangle_mesh_vert");
spirvGen.addShader(mod, b.path("shaders/default_lit.frag"), "default_lit");
spirvGen.addShader(mod, b.path("shaders/skybox/skybox.vert"), "skybox_vert");
spirvGen.addShader(mod, b.path("shaders/skybox/skybox.frag"), "skybox_frag");
spirvGen.addShader(mod, b.path("shaders/debug.vert"), "debug_vert");
spirvGen.addShader(mod, b.path("shaders/debug.frag"), "debug_frag");
// === simple little integration test ===
//
// this doesn't really do anything other than call a few functions
// to make sure that we properly linked everything
const test_step = b.step("test", "");
const tests = b.addTest(.{
.target = target,
.optimize = optimize,
.root_source_file = b.path("tests/tests.zig"),
.link_libc = true,
});
tests.root_module.addImport("graphics", mod);
for (dependencyList) |depName| {
const dep = b.dependency(depName, .{ .target = target, .optimize = optimize });
tests.root_module.addImport(depName, dep.module(depName));
}
const runArtifact = b.addRunArtifact(tests);
test_step.dependOn(&runArtifact.step);
}

View File

@ -1,21 +0,0 @@
.{
.name = "graphics",
.version = "0.0.0",
.dependencies = .{
//
.vulkan = .{ .path = "../../lib/vulkan" },
.vma = .{ .path = "../../lib/vma" },
.glfw3 = .{ .path = "../../lib/glfw3" },
.cgltf = .{ .path = "../../lib/cgltf" },
.objLoader = .{ .path = "../../lib/objLoader" },
.SpirvReflect = .{ .path = "../../lib/spirv-reflect-zig" },
.ozz = .{ .path = "../../lib/ozz" },
// core
.core = .{ .path = "../core" },
.assets = .{ .path = "../assets" },
.platform = .{ .path = "../platform" },
},
.paths = .{
"",
},
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@ -1,32 +0,0 @@
//glsl version 4.5
#version 450
layout (location = 0) in vec3 in_color;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in vec3 worldPosition;
layout (location = 0) out vec4 outFragColor;
layout (set = 0, binding = 0) uniform CameraBuffer{
mat4 view;
mat4 proj;
mat4 viewproj;
vec4 position;
} cameraData;
layout(set = 0, binding = 1) uniform SceneData{
vec4 fogColor; // w is for exponent
vec4 fogDistances; //x for min, y for max, zw unused.
vec4 ambientColor;
vec4 sunlightDirection; //w for sun power
vec4 sunlightColor;
} sceneData;
void main()
{
vec3 color = in_color.rgb;
float cameraDist = length(cameraData.position.xyz - worldPosition);
float opacity = (1.0) - (cameraDist / 300);
outFragColor = vec4(color, opacity * 1.0);
}

View File

@ -1,40 +0,0 @@
#version 460
layout (location = 0) in vec3 vPosition;
layout (location = 1) in vec3 vNormal;
layout (location = 2) in vec4 vColor;
layout (location = 3) in vec2 vTexCoord;
layout (location = 0) out vec3 outColor;
layout (location = 1) out vec2 texCoord;
layout (location = 2) out vec3 worldPosition;
layout (set = 0, binding = 0) uniform CameraBuffer{
mat4 view;
mat4 proj;
mat4 viewproj;
vec4 position;
} cameraData;
// size: 16 x 4 + 3 x 4 = 76 => 128 bytes per object per alignment
struct ObjectData {
mat4 model;
vec4 color;
};
layout(std140, set = 1, binding = 0) readonly buffer ObjectBuffer{
ObjectData objects[];
} objectBuffer;
void main()
{
ObjectData object = objectBuffer.objects[gl_BaseInstance];
mat4 modelMatrix = object.model;
mat4 final = (cameraData.viewproj * modelMatrix);
vec4 position = final * vec4(vPosition, 1.0f);
gl_Position = position;
outColor = object.color.xyz;
texCoord = vTexCoord;
vec4 modelPos = modelMatrix * vec4(vPosition, 1.0f);
worldPosition = modelPos.xyz;
}

View File

@ -1,47 +0,0 @@
//glsl version 4.5
#version 450
#extension GL_EXT_nonuniform_qualifier : require
layout (location = 0) in vec3 in_color;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in vec3 worldPosition;
layout (location = 3) flat in uint textureId;
layout (location = 4) flat in uint baseInstance;
layout (location = 0) out vec4 outFragColor;
#include "globalSet.glsl"
#include "sharedSsbo.glsl"
void main()
{
// outFragColor = vec4(in_color + 0.25 * sceneData.ambientColor.xyz,1.0f);
// outFragColor = vec4(texCoord.x, texCoord.y, 0.5f, 1.0f);
// vec4 color = texture(tex1, texCoord).xyzw;
vec4 color = texture(gTex[textureId], texCoord).xyzw;
if(color.w < 0.05f)
{
discard;
}
float cameraDist = length(cameraData.position.xyz - worldPosition);
float opacity = clamp((1.f) - (clamp(cameraDist - 300, 0, 300) / 300.f), 0.f, 1.f);
//float opacity = 1.0;
if(opacity < 0.05f)
{
discard;
}
// outFragColor = vec4(mix(sceneData.fogColor.xyz, color.xyz, opacity), color.w);
outFragColor = vec4(color.xyz, opacity);
//vec3 mixed = mix(normalize(vec3(0.5, 0.3, 0.2)) * 3, vec3(0.2, 0.2, 1) * 3, texCoord.y * 2);
//outFragColor = vec4(color.xyz, 1.0f);
//outFragColor = vec4(0.0, 1.0, 0.0, 1.0f);
}

View File

@ -1,18 +0,0 @@
layout (set = 0, binding = 0) uniform CameraBuffer{
mat4 view;
mat4 proj;
mat4 viewproj;
mat4 viewprojAlt;
vec4 position;
} cameraData;
layout(set = 0, binding = 1) uniform SceneData{
vec4 fogColor; // w is for exponent
vec4 fogDistances; //x for min, y for max, zw unused.
vec4 ambientColor;
vec4 sunlightDirection; //w for sun power
vec4 sunlightColor;
} sceneData;
layout(set = 0, binding = 2) uniform sampler2D[] gTex;

View File

@ -1,25 +0,0 @@
struct ObjectData {
mat4 model;
uint textureId;
int animation; // this is the index offset of the first matrix in the animation finals buffer.
uint flags0;
// packed flags0 flags;
// [0,0]: alwaysInFront
// [1,1]: useAltCamera
uint pad1;
};
layout(std140, set = 1, binding = 0) readonly buffer ObjectBuffer{
ObjectData objects[];
} objectBuffer;
uint flag0_AlwaysInFront(uint flags)
{
return flags & 0x1;
}
uint flag0_useAltFov(uint flags)
{
return (flags >> 1) & 0x1;
}

View File

@ -1,3 +0,0 @@
layout(std140, set = 1, binding = 1) readonly buffer BoneBuffer{
mat4 finals[];
} animationBuffer;

View File

@ -1,15 +0,0 @@
#version 450
#include "../globalSet.glsl"
layout(set = 1, binding = 0) uniform samplerCube cubemap;
layout (location = 0) in vec3 inUVW;
layout (location = 0) out vec4 outFragColor;
void main()
{
outFragColor = texture(cubemap, inUVW);
}

View File

@ -1,17 +0,0 @@
#version 450
#include "../vertexInput.glsl"
#include "../globalSet.glsl"
layout (location = 0) out vec3 outUVW;
void main()
{
outUVW = vPosition;
// Convert cubemap coordinates into Vulkan coordinate space
// Remove translation from view matrix
mat4 viewMat = mat4(mat3(cameraData.view));
gl_Position = cameraData.proj * viewMat * vec4(vPosition.xyz, 1.0);
}

View File

@ -1,66 +0,0 @@
#version 460
#include "vertexInput.glsl"
layout (location = 0) out vec3 outColor;
layout (location = 1) out vec2 texCoord;
layout (location = 2) out vec3 worldPosition;
layout (location = 3) flat out uint textureId;
layout (location = 4) flat out uint baseInstance;
#include "globalSet.glsl"
#include "sharedSsbo.glsl"
#include "skeletalBuffers.glsl"
void main()
{
vec3 vertexPos = vec3(0.0);
int animation = objectBuffer.objects[gl_BaseInstance].animation;
if(animation == -1)
{
vertexPos = vPosition;
}
else
{
uint animation = objectBuffer.objects[gl_BaseInstance].animation;
for(int i = 0; i < 4; i += 1)
{
uint boneIndex = bones[i];
float weight = float(weights[i]) / 255;
// this will depend on ozz's finals format
mat4 boneTransform = animationBuffer.finals[animation + boneIndex];
vertexPos += weight * ( boneTransform * vec4(vPosition, 1.0) ).xyz;
}
}
mat4 modelMatrix = objectBuffer.objects[gl_BaseInstance].model;
mat4 final;
if(flag0_useAltFov(objectBuffer.objects[gl_BaseInstance].flags0) == 1)
{
final = (cameraData.viewprojAlt * modelMatrix);
}
else
{
final = (cameraData.viewproj * modelMatrix);
}
vec4 position = final * vec4(vertexPos, 1.0f);
if( flag0_AlwaysInFront(objectBuffer.objects[gl_BaseInstance].flags0) == 1)
{
position.z *= 0.0001;
}
baseInstance = gl_BaseInstance;
gl_Position = position;
textureId = objectBuffer.objects[gl_BaseInstance].textureId;
outColor = vec3(vColor.x, vColor.y, vColor.z);
texCoord = vTexCoord;
worldPosition = (modelMatrix * vec4(0,0,0,1)).xyz;
}

View File

@ -1,7 +0,0 @@
#extension GL_EXT_shader_explicit_arithmetic_types_int8 : enable
layout (location = 0) in vec3 vPosition;
layout (location = 1) in vec3 vNormal;
layout (location = 2) in vec4 vColor;
layout (location = 3) in vec2 vTexCoord;
layout (location = 4) in u8vec4 bones;
layout (location = 5) in u8vec4 weights;

View File

@ -1,32 +0,0 @@
pixels: []u8,
extent: core.Vector2i,
const std = @import("std");
const core = @import("core");
const colors = core.colors;
pub fn init(allocator: std.mem.Allocator, extent: core.Vector2i) !@This() {
return .{
.pixels = try allocator.alignedAlloc(u8, 8, @intCast(extent.x * extent.y * 4)),
.extent = extent,
};
}
pub fn clear(self: *@This(), clearColor: colors.ColorRGBA8) void {
var as32: []u32 = undefined;
as32.len = self.pixels.len / 4;
as32.ptr = @alignCast(@ptrCast(self.pixels.ptr));
@memset(as32, @as(u32, @bitCast(clearColor)));
}
pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void {
allocator.free(self.pixels);
}
pub inline fn getPixel(self: *@This(), position: core.Vector2i) *colors.ColorRGBA8 {
const offset = position.x * position.y * 4;
const r: *u8 = &self.pixels[@intCast(offset)];
return @as(*colors.ColorRGBA8, @alignCast(@ptrCast(r)));
}

View File

@ -1,232 +0,0 @@
pub const AnimResolverRef = core.Reference(AnimResolverInterface);
pub const AnimResolverInterface = core.MakeInterface("AnimResolverVTable", struct {
// this tick function should evaluate the current state of the resolver
// and then update the animator's finals[] matrix list.
resolve: *const fn (*anyopaque, f64, *Animator) void,
onSkeletonSet: ?*const fn (*anyopaque, *Animator) void = null,
create: *const fn (std.mem.Allocator) core.EngineDataEventError!*anyopaque,
destroy: *const fn (*anyopaque) void,
pub fn Implement(comptime TargetType: type) @This() {
const Wrap = struct {
pub fn create(allocator: std.mem.Allocator) core.EngineDataEventError!*anyopaque {
const new = TargetType.create(allocator) catch return core.EngineDataEventError.BadInit;
return @ptrCast(new);
}
pub fn destroy(p: *anyopaque) void {
const ptr: *TargetType = @ptrCast(@alignCast(p));
ptr.destroy();
}
pub fn onSkeletonSet(p: *anyopaque, a: *Animator) void {
const ptr: *TargetType = @ptrCast(@alignCast(p));
ptr.onSkeletonSet(a) catch unreachable;
}
pub fn resolve(p: *anyopaque, dt: f64, a: *Animator) void {
const ptr: *TargetType = @ptrCast(@alignCast(p));
ptr.resolve(dt, a) catch unreachable;
}
};
return .{
.destroy = Wrap.destroy,
.create = Wrap.create,
.resolve = Wrap.resolve,
};
}
});
pub const AnimSampler = struct {
name: ?core.Name = null,
track: ?*AnimationTrack = null,
playbackRate: f32 = 1.0,
time: f32 = 0.0,
outputLocals: std.ArrayListUnmanaged(ozz.SoaTransform) = .{},
// other features
// paused: bool = false,
pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void {
self.outputLocals.deinit(allocator);
}
pub fn getOutput(self: *@This()) []ozz.SoaTransform {
return self.outputLocals.items;
}
pub fn sampleAndAdvance(self: *@This(), allocator: std.mem.Allocator, dt: f64, animator: *Animator) void {
self.sample(allocator, animator);
self.advance(dt);
}
pub fn advance(self: *@This(), dt: f64) void {
if (self.track == null) {
return;
}
FloatHelpers.updateTrackTime(&self.time, dt, self.playbackRate, self.track.?.endTime);
}
pub fn setName(self: *@This(), name: core.Name) void {
self.name = name;
self.track = null;
}
pub fn sample(self: *@This(), allocator: std.mem.Allocator, animator: *Animator) void {
if (self.name == null) {
return;
}
if (self.track == null) {
self.track = animation_system.gAnimationSys.animTracks.get(self.name.?.handle());
}
self.outputLocals.resize(allocator, animator.jointLength) catch return;
if (self.track) |track| {
if (track.endTime < 0.01) {
return;
}
animator.sampleAnimation(self.time, track, self.outputLocals.items);
}
}
};
pub const BlenderList = struct {
backing: std.mem.Allocator,
arena: std.heap.ArenaAllocator,
jobLayers: std.ArrayListUnmanaged(ozz.Layer) = .{},
jobLayersAdditive: std.ArrayListUnmanaged(ozz.Layer) = .{},
useAdditive: bool = false,
threshold: f32 = 0.01,
jointLength: usize = 0,
blendingJob: ozz.BlendingJob = .{},
pub fn create(backingAllocator: std.mem.Allocator) !*@This() {
const self = try backingAllocator.create(@This());
self.* = .{
.backing = backingAllocator,
.arena = std.heap.ArenaAllocator.init(backingAllocator),
};
return self;
}
pub fn destroy(self: *@This()) void {
self.arena.deinit();
self.backing.destroy(self);
}
pub fn updateRestPose(self: *@This(), animator: *Animator) !void {
if (animator.skeleton) |skeleton| {
self.jointLength = skeleton.sk.numJoints();
self.blendingJob.rest_pose = skeleton.sk.getRestPoseModel();
}
}
pub fn clearLayers(self: *@This()) void {
self.jobLayersAdditive.clearRetainingCapacity();
self.jobLayers.clearRetainingCapacity();
}
pub fn addLayer(self: *@This(), transform: []ozz.SoaTransform, weight: f32, settings: anytype) void {
const layer = self.jobLayers.addOne(self.arena.allocator()) catch unreachable;
layer.* = .{
.weight = weight,
.transform = ozz.makeSpan(transform),
};
_ = settings;
}
pub fn updateAndRun(self: *@This(), output: []ozz.SoaTransform) void {
self.updateBlendingJob();
self.runBlendingJob(output) catch return;
}
pub fn updateBlendingJob(self: *@This()) void {
self.blendingJob.threshold = self.threshold;
self.blendingJob.layers = ozz.makeSpan(self.jobLayers.items);
//self.blendingJob.additive_layers = if (self.useAdditive) ozz.makeSpan(self.jobLayersAdditive.items) else .{};
self.blendingJob.additive_layers = .{};
}
pub fn runBlendingJob(self: *@This(), output: []ozz.SoaTransform) !void {
self.blendingJob.output = ozz.makeSpan(output);
if (!self.blendingJob.run()) {
core.engine_logs("blending job failed");
return;
}
}
};
// resolver helpers
pub const FloatHelpers = struct {
pub inline fn updateTrackTime(target: *f32, dt: f64, rate: f32, endTime: f32) void {
target.* += @as(f32, @floatCast(dt)) * rate;
while (target.* > endTime) {
target.* -= endTime;
}
}
};
// samples a single animation, same as the default behaviour.
// used as a test for the resolver system
pub const SingleAnimationResolver = struct {
allocator: std.mem.Allocator,
locals: std.ArrayListUnmanaged(ozz.SoaTransform) = .{},
track: ?*AnimationTrack = null,
playback: f32 = 0.0,
playbackRate: f32 = 1.0,
pub const AnimResolverVTable = AnimResolverInterface.Implement(@This());
pub fn create(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.allocator = allocator,
};
return self;
}
pub fn onSkeletonSet(self: *@This(), animator: *Animator) !void {
if (animator.skeleton) |skeleton| {
try self.locals.resize(self.allocator, skeleton.sk.numSoaJoints());
}
}
pub fn resolve(self: *@This(), dt: f64, animator: *Animator) !void {
self.track = animator.track;
if (self.track == null) {
return;
}
const track = self.track.?;
if (track.endTime < 0.01) {
return;
}
FloatHelpers.updateTrackTime(&self.playback, dt, self.playbackRate, track.endTime);
animator.sampleAnimation(self.playback, track, self.locals.items);
animator.commitLocalToModel(self.locals.items);
animator.modelToFinal();
}
pub fn destroy(self: *@This()) void {
self.locals.deinit(self.allocator);
self.allocator.destroy(self);
}
};
const animation_system = @import("animationSystem.zig");
const Animator = animation_system.Animator;
const AnimationTrack = animation_system.AnimationTrack;
const core = @import("core");
const std = @import("std");
const ozz = @import("ozz");

View File

@ -1,480 +0,0 @@
// big main sy.itemsstem for animation
const ozz = @import("ozz");
const core = @import("core");
const std = @import("std");
pub const BoneHandle = enum(u8) { _ };
pub const Skeleton = struct {
sk: *ozz.Skeleton,
inverseBinds: std.ArrayListUnmanaged(core.Mat) = .{},
jointMapping: std.StringHashMapUnmanaged(u8) = .{},
pub fn buildJointMap(self: *@This(), allocator: std.mem.Allocator) !void {
for (self.sk.getJointsList(), 0..) |jointName, i| {
// std.debug.print("jointName {d} {s}\n", .{ i, jointName });
const str = std.mem.span(jointName);
try self.jointMapping.put(allocator, str, @intCast(i));
}
}
pub fn getBoneHandleByName(self: @This(), string: []const u8) ?BoneHandle {
if (self.jointMapping.get(string)) |x| {
return @enumFromInt(x);
} else {
return null;
}
}
pub fn deinit(self: *@This()) void {
self.sk.destroy();
}
};
pub const AnimationTrack = struct {
animation: *ozz.Animation,
endTime: f32 = 1.0,
pub fn deinit(self: *@This()) void {
self.animation.destroy();
}
};
pub const PlaybackTrack = struct {
track: ?*AnimationTrack = null,
playback: f32 = 0.0,
playbackRate: f32 = 1.0,
};
pub const Animator = struct {
jointRemap: ?[]u8 = null,
animationName: ?core.Name = null,
skeleton: ?*Skeleton = null,
skeletonName: ?core.Name = null,
sjc: *ozz.SamplingJobContext = undefined,
track: ?*AnimationTrack = null,
playback: f32 = 0.0,
playbackRate: f32 = 1.0,
// todo.. implement blending
// animations: [4]*ozz.Animation = undefined,
// timelines: [4]f32 = .{ 0, 0, 0, 0 },
animationCount: u32 = 0,
locals: std.ArrayListUnmanaged(ozz.SoaTransform) = .{},
models: std.ArrayListUnmanaged(ozz.Float4x4) = .{},
finals: std.ArrayListUnmanaged(core.Mat) = .{},
finalsSpan: core.Span = undefined,
entity: core.Entity = undefined,
jointLength: usize = 0,
resolverRef: ?AnimResolverRef = null,
pub var allocator: std.mem.Allocator = undefined;
// oh god if I want to support multiple animation blending...
// maybe the kernel should contain a fixed amount of animations?
pub fn initECS(self: *@This(), handle: core.SetHandle) void {
// get the mesh component
self.entity = core.Entity{ .handle = handle };
if (self.entity.get(graphics.StaticMesh)) |mesh| {
mesh.animated = true; //todo
mesh.animator = self;
self.sjc = ozz.SamplingJobContext.createMaxTracks(256);
} else {
@panic("animator added to an entity that does not have a mesh component");
}
}
pub fn getBoneTransform(self: *@This(), handle: BoneHandle) core.Mat {
return @bitCast(self.models.items[@intFromEnum(handle)]);
}
pub fn setSkeletonByName(self: *@This(), skName: core.Name) !void {
if (self.skeleton != null) {
// return the previous span and allocate a new one.
gAnimationSys.slots.removeSpan(self.finalsSpan);
}
self.skeletonName = skName;
self.skeleton = gAnimationSys.skeletons.get(self.skeletonName.?.handle()).?;
const numJoints = self.skeleton.?.sk.numJoints();
self.jointLength = numJoints;
self.sjc.resize(@intCast(numJoints));
try self.locals.resize(allocator, self.skeleton.?.sk.numSoaJoints());
try self.models.resize(allocator, numJoints);
try self.finals.resize(allocator, numJoints);
self.finalsSpan = try gAnimationSys.slots.allocate(@intCast(numJoints));
if (self.resolverRef) |ref| {
if (ref.vtable.onSkeletonSet) |f| {
f(ref.ptr, self);
}
}
self.jointRemap = null;
}
pub fn setSkeleton(self: *@This(), skeleton: []const u8) void {
self.setSkeletonByName(core.MakeName(skeleton)) catch unreachable;
}
pub fn addResolver(self: *@This(), comptime Resolver: type) !*Resolver {
const resolver = try Resolver.create(allocator);
try resolver.onSkeletonSet(self);
self.resolverRef = core.refFromPtr(AnimResolverInterface, resolver);
return resolver;
}
pub fn removeResolver(self: *@This()) void {
if (self.resolverRef) |ref| {
ref.vtable.destroy(ref.ptr);
self.resolverRef = null;
}
}
pub fn update(self: *@This(), dt: f64) void {
if (self.skeleton == null) {
return;
}
// if a resolver is present, use that to update my the finals instead of the default function below
if (self.resolverRef) |ref| {
ref.vtable.resolve(ref.ptr, dt, self);
return;
}
if (!self.defaultSample(dt)) {
return;
}
self.modelToFinal();
}
fn defaultSample(self: *@This(), dt: f64) bool {
if (self.track == null)
return false;
const track = self.track.?;
if (track.endTime < 0.01)
return false;
const skeleton = self.skeleton.?;
self.playback += @as(f32, @floatCast(dt)) * self.playbackRate;
while (self.playback > track.endTime) {
self.playback -= track.endTime;
}
var samplingJob: ozz.SamplingJob = .{
.ratio = self.playback / track.endTime,
.animation = track.animation,
.context = self.sjc,
.output = ozz.makeSpan(self.locals.items),
};
if (!samplingJob.run()) {
core.engine_errs("sampling job failed");
return false;
}
var ltmJob: ozz.LocalToModelJob = .{
.skeleton = skeleton.sk,
.input = ozz.makeSpan(self.locals.items),
.output = ozz.makeSpan(self.models.items),
};
if (!ltmJob.run()) {
core.engine_errs("local to model job failed");
return false;
}
return true;
}
pub fn commitLocalToModel(self: *@This(), input: []ozz.SoaTransform) void {
self.localToModel(input, self.models.items);
}
pub fn localToModel(self: *@This(), input: []ozz.SoaTransform, output: []ozz.Float4x4) void {
var ltmJob: ozz.LocalToModelJob = .{
.skeleton = self.skeleton.?.sk,
.input = ozz.makeSpan(input),
.output = ozz.makeSpan(output),
};
if (!ltmJob.run()) {
core.engine_errs("local to model job failed");
return;
}
}
pub fn sampleAnimation(self: *@This(), time: f32, track: *AnimationTrack, output: []ozz.SoaTransform) void {
var samplingJob: ozz.SamplingJob = .{
.ratio = time / track.endTime,
.animation = track.animation,
.context = self.sjc,
.output = ozz.makeSpan(output),
};
if (!samplingJob.run()) {
core.engine_errs("sampling job failed");
return;
}
}
pub fn modelToFinal(self: *@This()) void {
if (self.jointRemap == null) {
if (self.entity.get(graphics.StaticMesh)) |meshComponent| {
if (meshComponent.mesh) |mesh| {
self.jointRemap = mesh.jointRemap;
}
}
}
const skeleton = self.skeleton.?;
for (self.models.items, 0..) |model, i| {
const transform: core.Mat = @bitCast(model);
// const p: core.zm.Vec = .{ 0, 0, 0, 1 };
// graphics.debugSphere(core.Vectorf.fromZm(core.zm.mul(p, transform)), 0.03, .{
// .color = if (i == 15) .{ .x = 1 } else .{ .y = 1 },
// });
const final = core.zm.mul(skeleton.inverseBinds.items[i], transform);
// joint remap ozz -> gltf
if (self.jointRemap) |jr| {
// core.engine_log("{d} xx {d}", .{ i, jr[i] });
self.finals.items[@intCast(jr[i])] = final;
} else {
self.finals.items[i] = final;
}
// core.engine_log(
// "[{d}] {d} {d} {d} {d}, {d} {d} {d} {d}",
// .{ i, transform[0][0], transform[0][1], transform[0][2], transform[0][3], transform[1][0], transform[1][1], transform[1][2], transform[1][3] },
// );
}
}
pub fn setAnimationByName(self: *@This(), _name: core.Name) !void {
var name = _name;
self.track = gAnimationSys.animTracks.get(name.handle());
}
pub fn setAnimation(self: *@This(), path: []const u8) void {
const name = core.MakeName(path);
self.setAnimationByName(name) catch unreachable;
}
pub fn deinit(self: *@This()) void {
self.sjc.destroy();
self.removeResolver();
self.finals.deinit(allocator);
self.locals.deinit(allocator);
self.models.deinit(allocator);
}
pub var BaseContainer: *core.SparseMap(@This()) = undefined;
pub const ComponentName = "Animator";
pub const ScriptExports: []const []const u8 = &.{};
};
pub const AnimationSystem = struct {
backingAllocator: std.mem.Allocator,
arena: std.heap.ArenaAllocator,
slots: MergedSpans,
// Only AnimationTrack and Skeletons are made using the ArenaAllocator
animTracks: std.AutoHashMapUnmanaged(u32, *AnimationTrack) = .{},
skeletons: std.AutoHashMapUnmanaged(u32, *Skeleton) = .{},
sharedArena: [2]std.heap.ArenaAllocator, // could be a good usecase for a fat bump arena
shared: [2]std.ArrayListUnmanaged(MatrixUploads) = .{ .{}, .{} },
sharedLocks: [2]std.Thread.Mutex = .{ .{}, .{} }, // could be a good usecase for a fat bump arena
pub const MatrixUploads = struct {
offset: u32,
matrices: std.ArrayListUnmanaged(core.Mat) = .{},
};
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
pub const RendererInterfaceVTable = graphics.RendererInterface.from(@This());
pub fn preTick(self: *@This(), dt: f64) !void {
_ = self;
var z1 = core.tracy.ZoneN(@src(), "animation system tick");
defer z1.End();
for (Animator.BaseContainer.list.items) |animator| {
animator.update(dt);
}
}
pub fn newAnimTrack(self: *@This(), _name: core.Name, anim: *ozz.Animation) !void {
var name = _name;
const new = try self.arenaAllocator().create(AnimationTrack);
new.* = .{
.animation = anim,
.endTime = anim.getDuration(),
};
try self.animTracks.put(self.backingAllocator, name.handle(), new);
}
pub fn newSkeleton(self: *@This(), _name: core.Name, sk: *ozz.Skeleton) !void {
const new = try self.arenaAllocator().create(Skeleton);
new.* = .{
.sk = sk,
};
var name = _name;
try new.buildJointMap(self.arenaAllocator());
try new.inverseBinds.resize(self.arenaAllocator(), new.sk.numJoints());
if (new.inverseBinds.items.len > 256) {
@panic("too many bones in skeleton, not supported");
}
var bindModels = std.ArrayList(ozz.Float4x4).init(self.backingAllocator);
defer bindModels.deinit();
try bindModels.resize(new.sk.numJoints());
var ltmJob: ozz.LocalToModelJob = .{
.skeleton = new.sk,
.input = new.sk.getRestPoseModel(),
.output = ozz.makeSpan(bindModels.items),
};
core.engine_log("creating bind pose {d} joints", .{new.inverseBinds.items.len});
if (!ltmJob.run()) {
core.engine_logs("unable to get bind pose");
return error.UnableToLoad;
}
for (bindModels.items, 0..) |bind, i| {
// const p: core.zm.Vec = .{ 0, 0, 0, 1 };
// graphics.debugSphere(core.Vectorf.fromZm(core.zm.mul(p, @as(core.Mat, @bitCast(bind)))), 0.1, .{ .duration = 100 });
new.inverseBinds.items[i] = core.zm.inverse(@as(core.Mat, @bitCast(bind)));
}
try self.skeletons.put(self.backingAllocator, name.handle(), new);
}
pub fn arenaAllocator(self: *@This()) std.mem.Allocator {
return self.arena.allocator();
}
pub fn getShared(self: @This(), fi: u32) []const MatrixUploads {
return self.shared[fi].items;
}
pub fn sendShared(self: *@This(), frameIndex: u32) void {
const fi: usize = @intCast(frameIndex);
self.sharedLocks[fi].lock();
defer self.sharedLocks[fi].unlock();
_ = self.sharedArena[fi].reset(.retain_capacity);
const allocator = self.sharedArena[fi].allocator();
const shared = &self.shared[fi];
shared.* = .{};
for (Animator.BaseContainer.list.items) |animator| {
var upload: MatrixUploads = .{ .offset = animator.finalsSpan.start };
// core.engine_log(
// "finalsSpan size offset{d} {d} animator finals {d}\n",
// .{
// animator.finalsSpan.start,
// animator.finalsSpan.size,
// animator.finals.items.len
// });
upload.matrices.resize(allocator, animator.finalsSpan.size) catch unreachable;
for (animator.finals.items, 0..) |final, i| {
upload.matrices.items[i] = final;
}
shared.append(allocator, upload) catch unreachable;
}
}
pub fn init(alloc: std.mem.Allocator) !*@This() {
const self = try alloc.create(@This());
self.* = .{
.backingAllocator = alloc,
.arena = std.heap.ArenaAllocator.init(alloc),
.sharedArena = .{
std.heap.ArenaAllocator.init(alloc),
std.heap.ArenaAllocator.init(alloc),
},
.slots = try MergedSpans.init(alloc, vk_constants.MAX_SKIN_SLOTS),
};
gAnimationSys = self;
Animator.allocator = alloc;
try core.defineComponent(Animator, alloc);
return self;
}
pub fn deinit(self: *@This()) void {
core.engine_logs("deinitializing animation system");
{
core.engine_log("skeleton count {d}", .{self.skeletons.count()});
var iter = self.skeletons.iterator();
while (iter.next()) |i| {
i.value_ptr.*.deinit();
}
}
{
core.engine_log("animTracks count {d}", .{self.animTracks.count()});
var iter = self.animTracks.iterator();
while (iter.next()) |i| {
i.value_ptr.*.deinit();
}
}
for (self.sharedArena) |arena| {
arena.deinit();
}
for (Animator.BaseContainer.list.items) |animator| {
animator.deinit();
}
self.slots.deinit();
core.undefineComponent(Animator);
self.arena.deinit();
self.skeletons.deinit(self.backingAllocator);
self.animTracks.deinit(self.backingAllocator);
self.backingAllocator.destroy(self);
}
};
pub var gAnimationSys: *AnimationSystem = undefined;
pub fn getSkeletonByName(_name: core.Name) ?*Skeleton {
var name = _name;
return gAnimationSys.skeletons.get(name.handle());
}
const graphics = @import("../graphics.zig");
const MergedSpans = core.MergedSpans;
const vk_constants = @import("../vk_constants.zig");
const anim_resolver = @import("animResolver.zig");
const AnimResolverRef = anim_resolver.AnimResolverRef;
const AnimResolverInterface = anim_resolver.AnimResolverInterface;

View File

@ -1,86 +0,0 @@
pub const AnimationLoader = struct {
pub var LoaderInterfaceVTable: assets.AssetLoaderInterface = assets.AssetLoaderInterface.from("Animation", @This());
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
sys: *animation_system.AnimationSystem,
pub fn discardAll(self: *@This()) void {
_ = self;
}
pub fn loadAsset(self: *@This(), assetRef: assets.AssetRef, propertiesBag: ?assets.AssetPropertiesBag) assets.AssetLoaderError!void {
const animation = ozz.Animation.create();
const mapping = core.fs().loadFile(propertiesBag.?.path) catch return error.UnableToLoad;
defer core.fs().unmap(mapping);
animation.loadFromBytes(mapping.bytes);
self.sys.newAnimTrack(assetRef.name, animation) catch return error.UnableToLoad;
}
pub fn init(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.sys = animation_system.gAnimationSys,
};
return self;
}
pub fn destroy(self: *@This(), allocator: std.mem.Allocator) void {
allocator.destroy(self);
}
};
pub const SkeletonLoader = struct {
pub var LoaderInterfaceVTable: assets.AssetLoaderInterface = assets.AssetLoaderInterface.from("Skeleton", @This());
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
sys: *animation_system.AnimationSystem,
pub fn discardAll(self: *@This()) void {
_ = self;
}
pub fn loadAsset(self: *@This(), assetRef: assets.AssetRef, propertiesBag: ?assets.AssetPropertiesBag) assets.AssetLoaderError!void {
const sk = ozz.Skeleton.create();
const mapping = core.fs().loadFile(propertiesBag.?.path) catch return error.UnableToLoad;
defer core.fs().unmap(mapping);
sk.loadFromBytes(mapping.bytes);
self.sys.newSkeleton(assetRef.name, sk) catch return error.UnableToLoad;
}
pub fn init(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.sys = animation_system.gAnimationSys,
};
return self;
}
pub fn destroy(self: *@This(), allocator: std.mem.Allocator) void {
allocator.destroy(self);
}
};
pub var gSkeletonLoader: *SkeletonLoader = undefined;
pub var gAnimationLoader: *AnimationLoader = undefined;
pub fn initLoaders() !void {
gSkeletonLoader = try core.createObject(SkeletonLoader, .{});
gAnimationLoader = try core.createObject(AnimationLoader, .{});
try assets.gAssetSys.registerLoader(gSkeletonLoader);
try assets.gAssetSys.registerLoader(gAnimationLoader);
}
const animation_system = @import("animationSystem.zig");
const assets = @import("assets");
const core = @import("core");
const std = @import("std");
const ozz = @import("ozz");

View File

@ -1,239 +0,0 @@
const MeshConfig = struct {
info: CookInfo = .{ .assetType = "Mesh" }, // there must always be a CookInfo field
sourceType: []const u8 = "obj",
animated: bool = false,
};
const extList = [_][]const u8{ "gltf", "obj", "glb" };
pub fn generateFunction(allocator: std.mem.Allocator, path: []const u8, out: *std.ArrayList(u8)) GenerateError!void {
_ = allocator;
out.clearRetainingCapacity();
const ext = core.getFileExtension(path)[1..];
var config: MeshConfig = .{};
for (extList) |e| {
if (std.mem.eql(u8, e, ext)) {
config.sourceType = e;
if (std.mem.eql(u8, e, "glb")) {
config.sourceType = "gltf";
}
}
}
std.json.stringify(
config,
.{ .whitespace = .indent_4 },
out.writer(),
) catch return GenerateError.UnableToGenerate;
}
fn cookObj(allocator: std.mem.Allocator, dir: std.fs.Dir, path: []const u8) cook.CookResult {
const rawFileBytes = cook.loadFileAlloc(allocator, dir, path) catch unreachable;
defer allocator.free(rawFileBytes);
var out = std.ArrayList(u8).init(allocator);
var vertices = std.ArrayList(Vertex).init(allocator);
defer vertices.deinit();
var objs = obj.loadObjBytes(rawFileBytes, allocator) catch unreachable;
defer objs.deinit();
if (objs.meshes.items.len > 0) {
mesh.loadObjMeshVertices(&vertices, objs.meshes.items[0]) catch unreachable;
for (vertices.items) |vert| {
out.appendSlice(&@as([@sizeOf(Vertex)]u8, @bitCast(vert))) catch unreachable;
}
return .{
.bytes = out,
.result = .Success,
};
} else {
return .{
.bytes = out,
.result = .Failure,
};
}
}
fn ensureGltf2ozz(allocator: std.mem.Allocator) !void {
const suffix = if (builtin.os.tag == .windows) ".exe" else "";
std.fs.cwd().access("zig-out/tools/gltf2ozz" ++ suffix, .{}) catch {
const argv: []const []const u8 = &.{ "zig", "build", "tools" };
core.engine_log("gltf2ozz missing, building it...", .{});
var child = std.process.Child.init(argv, allocator);
child.stdin_behavior = .Ignore;
child.stdout_behavior = .Pipe;
child.stderr_behavior = .Pipe;
child.cwd = ".";
switch (try child.spawnAndWait()) {
.Exited => |value| {
if (value == 0) {
core.engine_log("gltf2ozz built", .{});
} else {
core.engine_logs("unable to build gltf2ozz");
}
},
.Signal => {
core.engine_logs("unable to build gltf2ozz");
},
.Stopped => {},
.Unknown => {
unreachable;
},
}
return;
};
core.engine_log("gltf2ozz found", .{});
}
fn cookAnimations(allocator: std.mem.Allocator, dir: std.fs.Dir, path: []const u8, config: MeshConfig) !void {
_ = config;
// 1. check if it has an associated .ozzconfig file.
const gltf2OzzAbs = try std.fs.cwd().realpathAlloc(allocator, "zig-out/tools/gltf2ozz.exe");
defer allocator.free(gltf2OzzAbs);
const ozzconfig = try std.fmt.allocPrint(allocator, "{s}.ozzconfig", .{path});
defer allocator.free(ozzconfig);
const fileArg = try std.fmt.allocPrint(allocator, "--file={s}", .{core.getBasePath(path)});
defer allocator.free(fileArg);
const configArg = try std.fmt.allocPrint(allocator, "--config_file={s}", .{core.getBasePath(ozzconfig)});
defer allocator.free(configArg);
// todo, fix this later, idrc right now.
const newConfigArg = try std.fmt.allocPrint(allocator, "--config_dump_reference={s}", .{core.getBasePath(ozzconfig)});
defer allocator.free(newConfigArg);
const absFile = try dir.realpathAlloc(allocator, path);
defer allocator.free(absFile);
var argv: []const []const u8 = &.{
gltf2OzzAbs,
fileArg,
configArg,
};
dir.access(ozzconfig, .{}) catch {
argv = &.{
gltf2OzzAbs,
fileArg,
newConfigArg,
};
core.engine_log("creating ozz config for file, marked as animated but no animation data", .{});
};
// std.debug.print("{s} {s} {s} cwd = {s}\n", .{ argv[0], argv[1], argv[2], core.getFolder(absFile) });
const result = try std.process.Child.run(.{
.argv = argv,
.allocator = allocator,
.cwd = core.getFolder(absFile),
.max_output_bytes = 150 * 1024 * 1024,
});
defer allocator.free(result.stdout);
defer allocator.free(result.stderr);
var success: bool = true;
switch (result.term) {
.Exited => |value| {
if (value != 0) {
success = false;
}
},
.Signal => {
success = false;
},
.Stopped => {
success = false;
// no-op should be ok?
},
.Unknown => {
unreachable;
},
}
if (success) {
core.engine_log("generated animations for {s}", .{path});
} else {
core.engine_log("error generating animations {s} stdout:\n{s}\n stderr:{s}\n", .{ path, result.stdout, result.stderr });
}
// 2. if so, run it through gltf2ozz with that file.
}
fn cookGltf(allocator: std.mem.Allocator, dir: std.fs.Dir, path: []const u8, config: MeshConfig) cook.CookResult {
core.engine_logs("gltf cooking not implemeted");
const out = std.ArrayList(u8).init(allocator);
// check if it's animated. if it's animated, then invoke gltf2ozz and create a .ozzconfig file and
// make a subfolder called
if (config.animated) {
// if gltf2ozz isn't there then we have to call zig build tools
ensureGltf2ozz(allocator) catch unreachable;
cookAnimations(allocator, dir, path, config) catch unreachable;
}
return .{ .bytes = out, .result = .Failure };
}
pub fn cookFunction(
allocator: std.mem.Allocator,
dir: std.fs.Dir,
path: []const u8,
params: cook.CookParams,
) cook.CookResult {
core.engine_log("{s}", .{params.cookFileName});
const fc = cook.loadFileAlloc(allocator, dir, params.cookFileName) catch unreachable;
defer allocator.free(fc);
const config = std.json.parseFromSlice(MeshConfig, allocator, fc[0 .. fc.len - 1], .{}) catch unreachable;
defer config.deinit();
if (std.mem.eql(u8, config.value.sourceType, "obj")) {
return cookObj(allocator, dir, path);
} else {
return cookGltf(allocator, dir, path, config.value);
}
}
pub fn initCooker(allocator: std.mem.Allocator) !void {
_ = allocator;
const registry = assets.cook.getRegistry();
try registry.install("Mesh", generateFunction, cookFunction, &.{
".obj",
".gltf",
".glb",
});
}
pub fn deinitCooker() void {
//
}
const std = @import("std");
const assets = @import("assets");
const cook = assets.cook;
const CookInfo = assets.cook.CookInfo;
const GenerateError = assets.cook.GenerateError;
const core = @import("core");
const obj = @import("objLoader");
const builtin = @import("builtin");
const mesh = @import("../mesh.zig");
const Mesh = mesh.Mesh;
const Vertex = mesh.MeshVertex;

View File

@ -1,60 +0,0 @@
const TextureConfig = struct {
info: CookInfo = .{ .assetType = "Texture" }, // there must always be a CookInfo field
sourceType: []const u8 = "png",
};
pub fn generateFunction(allocator: std.mem.Allocator, path: []const u8, out: *std.ArrayList(u8)) GenerateError!void {
_ = allocator;
_ = path;
out.clearRetainingCapacity();
std.json.stringify(
TextureConfig{},
.{ .whitespace = .indent_4 },
out.writer(),
) catch return GenerateError.UnableToGenerate;
}
pub fn cookFunction(
allocator: std.mem.Allocator,
dir: std.fs.Dir,
path: []const u8,
params: cook.CookParams,
) cook.CookResult {
_ = params;
const rawFileBytes = cook.loadFileAlloc(allocator, dir, path) catch unreachable;
defer allocator.free(rawFileBytes);
// 1. load the file, and create a bytes buffer
var contents = png.PngContents.initFromBytes(allocator, path, rawFileBytes) catch unreachable;
defer contents.deinit();
// png.PngContents.initFromBytes(allocator: std.mem.Allocator, pathName: []const u8, pngFileContents: []const u8)
// 2. use the PngContents function to cook it.
return .{
.bytes = contents.toBuffer() catch unreachable,
.result = .Success,
};
}
pub fn initCooker(allocator: std.mem.Allocator) !void {
_ = allocator;
const registry = assets.cook.getRegistry();
try registry.install("Texture", generateFunction, cookFunction, &.{
".png",
});
}
pub fn deinitCooker() void {
//
}
const std = @import("std");
const assets = @import("assets");
const cook = assets.cook;
const CookInfo = assets.cook.CookInfo;
const GenerateError = assets.cook.GenerateError;
const core = @import("core");
const png = core.png;

View File

@ -1,421 +0,0 @@
const std = @import("std");
const vk = @import("vulkan");
const vkd_utils = @import("vk_renderer/vkd_utils.zig");
const core = @import("core");
const graphics = @import("graphics.zig");
const assets = @import("assets");
const debug_vert = @import("debug_vert");
const debug_frag = @import("debug_frag");
const tracy = core.tracy;
const gpd = graphics.gpu_pipe_data;
pub const DebugLine = struct {
start: core.Vectorf,
end: core.Vectorf,
pub fn resolve(self: @This(), _: anytype) core.Transform {
var delta = self.start.sub(self.end);
const d = delta.normalize();
const axz = std.math.atan2(-d.z, d.x) + core.radians(180.0);
const ay = -std.math.asin(d.y);
const mat1 = core.zm.matFromRollPitchYaw(0, 0, ay);
const mat2 = core.zm.rotationY(axz);
const len = delta.length();
return core.zm.mul(core.zm.mul(
core.zm.mul(mat1, mat2),
core.zm.scaling(len, len, len),
), core.zm.translationV(self.start.toZm()));
}
};
pub const DebugSphere = struct {
position: core.Vectorf,
radius: f32,
rotation: core.Quat,
pub fn resolve(self: @This(), _: anytype) core.Transform {
return core.zm.mul(core.zm.mul(
core.zm.matFromQuat(self.rotation),
core.zm.scaling(self.radius, self.radius, self.radius),
), core.zm.translationV(self.position.toZm()));
}
};
pub const DebugBox = struct {
position: core.Vectorf,
extents: core.Vectorf,
rotation: core.Quat,
pub fn resolve(self: @This(), _: anytype) core.Transform {
return core.zm.mul(core.zm.mul(
core.zm.matFromQuat(self.rotation),
core.zm.scalingV(self.extents.toZm()),
), core.zm.translationV(self.position.toZm()));
}
};
const DebugPrimitiveType = enum(u8) {
line = 0,
sphere = 1,
box = 2,
};
pub const DebugPrimitive = struct {
primitive: union(DebugPrimitiveType) {
line: DebugLine,
sphere: DebugSphere,
box: DebugBox,
},
color: core.Vectorf = .{ .x = 0.0, .y = 1.0, .z = 0.0 },
duration: f32 = 0.0,
pub fn resolve(self: @This()) core.Transform {
// comptime core.asserts(@sizeOf(DebugPrimitiveGpu) == DebugPrimitiveGpu.TargetSize, "");
switch (self.primitive) {
.line => |inner| {
return inner.resolve(.{});
},
.sphere => |inner| {
return inner.resolve(.{});
},
.box => |inner| {
return inner.resolve(.{});
},
}
unreachable;
// return core.implement_func_for_tagged_union_nonull(self.primitive, "resolve", core.Transform, .{});
}
};
const DebugPrimitiveGpu = struct {
const UnpaddedSize = @sizeOf(core.Transform) + @sizeOf(core.Vectorf);
const TargetSize = 80;
model: core.Transform,
color: core.Vectorf,
pad: [TargetSize - UnpaddedSize]u8 = std.mem.zeroes([TargetSize - UnpaddedSize]u8),
};
const DebugDrawSharedInstance = struct {};
const DebugSharedData = struct {
drawsThisFrame: std.ArrayListUnmanaged(DebugPrimitive) = .{},
lock: std.Thread.Mutex = .{},
pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void {
self.drawsThisFrame.deinit(allocator);
}
};
const objectCount = 2048;
// Debug draw system also an example of how to do plugins in this engine
pub const DebugDrawSubsystem = struct {
// Interfaces and tables
pub const RendererInterfaceVTable = graphics.RendererInterface.from(@This());
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
// Member functions
allocator: std.mem.Allocator,
debugDraws: core.RingQueueU(DebugPrimitive),
meshes: [@as(usize, @intCast(@intFromEnum(DebugPrimitiveType.box) + 1))]core.Name = .{ undefined, undefined, undefined },
gc: *graphics.NeonVkContext = undefined,
pipeData: gpd.GpuPipeData = undefined,
mappedBuffers: []gpd.GpuMappingData(DebugPrimitiveGpu) = undefined,
material: *graphics.Material = undefined,
materialName: core.Name = core.Name.MakeComptime("mat_debugsys"),
deltaTime: f64 = 0,
sharedData: [graphics.NumFrames]DebugSharedData = .{ .{}, .{} },
indirectStaging: graphics.NeonVkBuffer = undefined,
indirectGpu: graphics.NeonVkBuffer = undefined,
const Primitives = [_]assets.AssetImportReference{
assets.MakeImportRef("Mesh", "m_primitive_sphere", "meshes/primitive_sphere.obj"),
assets.MakeImportRef("Mesh", "m_primitive_box", "meshes/primitive_box.obj"),
assets.MakeImportRef("Mesh", "m_primitive_line", "meshes/primitive_line.obj"),
};
pub fn prepareSubsystem(self: *@This(), gc: *graphics.NeonVkContext) !void {
self.gc = gc;
// assign debug meshes
self.meshes[@as(usize, @intCast(@intFromEnum(DebugPrimitiveType.sphere)))] = core.MakeName("m_primitive_sphere");
self.meshes[@as(usize, @intCast(@intFromEnum(DebugPrimitiveType.box)))] = core.MakeName("m_primitive_box");
self.meshes[@as(usize, @intCast(@intFromEnum(DebugPrimitiveType.line)))] = core.MakeName("m_primitive_line");
try self.createPipeData();
try self.createMaterial();
// create indirect command buffers
self.indirectStaging = try gc.vkAllocator.createStagingBuffer(4096 * @sizeOf(vk.DrawIndexedIndirectCommand), "debug draw indirect staging buffer");
self.indirectGpu = try gc.vkAllocator.createIndirectCommandBuffer(4096 * @sizeOf(vk.DrawIndexedIndirectCommand), "debug draw indirect command buffer");
}
pub fn uploadIndirectCommands(self: *@This(), cmd: vk.CommandBuffer, count: u32) void {
vkd_utils.copyStagingSlice(vk.DrawIndexedIndirectCommand, cmd, .{
.src = &self.indirectStaging,
.dst = &self.indirectGpu,
.size = count,
});
}
pub fn createPipeData(self: *@This()) !void {
var dataBuilder = gpd.GpuPipeDataBuilder.init(self.allocator, self.gc);
defer dataBuilder.deinit();
dataBuilder.setObjectCount(objectCount);
try dataBuilder.addBufferBinding(DebugPrimitiveGpu, .storage_buffer, .{ .vertex_bit = true }, .storageBuffer);
self.pipeData = try dataBuilder.build("debug draws");
self.mappedBuffers = try self.pipeData.mapBuffers(self.gc, DebugPrimitiveGpu, 0);
}
pub fn createMaterial(self: *@This()) !void {
var gc: *graphics.NeonVkContext = self.gc;
const vert_spv = debug_vert.spv();
const frag_spv = debug_frag.spv();
var pipelineBuilder = try graphics.NeonVkPipelineBuilder.init(
gc.dev,
gc.vkd,
gc.allocator,
gc.vkAllocator,
vert_spv,
frag_spv,
);
defer pipelineBuilder.deinit();
try pipelineBuilder.add_mesh_description();
try pipelineBuilder.add_layout(self.gc.globalDescriptorLayout);
try pipelineBuilder.add_layout(self.pipeData.descriptorSetLayout);
try pipelineBuilder.add_depth_stencil();
pipelineBuilder.set_polygon_mode(.line);
pipelineBuilder.set_topology(.triangle_list);
try pipelineBuilder.init_triangle_pipeline(gc.actual_extent);
const materialName = self.materialName;
const material = try gc.allocator.create(graphics.Material);
material.* = graphics.Material{
.materialName = materialName,
.pipeline = (try pipelineBuilder.build(gc.renderPass)).?,
.layout = pipelineBuilder.pipelineLayout,
};
try gc.add_material(material);
self.material = material;
}
// Renderer Ineterface Implementation
// pub fn preDraw(self: *@This(), frameId: usize) void {
// var zone = tracy.ZoneN(@src(), "Debug draw renderer");
// defer zone.End();
// const count: usize = self.debugDraws.count();
// var offset: usize = 0;
// while (offset < count) : (offset += 1) {
// const primitive = self.debugDraws.at(offset).?;
// const transform = primitive.resolve();
// const color = primitive.color;
// const object = &self.mappedBuffers[frameId].objects[offset];
// object.*.color = color;
// object.*.model = transform;
// }
// }
pub fn tick(self: *@This(), dt: f64) void {
self.deltaTime = dt;
}
pub fn sendShared(self: *@This(), frameIndex: u32) void {
var zone = tracy.ZoneN(@src(), "debug draw- uploading shared");
defer zone.End();
self.sharedData[frameIndex].lock.lock();
defer self.sharedData[frameIndex].lock.unlock();
var offset: usize = 0;
const count = self.debugDraws.count();
self.sharedData[frameIndex].drawsThisFrame.clearRetainingCapacity();
while (offset < count) {
var primitive: DebugPrimitive = self.debugDraws.pop().?;
self.sharedData[frameIndex].drawsThisFrame.append(self.allocator, primitive) catch unreachable;
primitive.duration -= @as(f32, @floatCast(self.deltaTime));
offset += 1;
if (primitive.duration >= 0) {
// push this primitive so that it goes to the next frame
self.debugDraws.push(primitive) catch continue;
}
}
}
pub fn rtPreDraw(self: *@This(), rt: *graphics.RenderThread, cmd: vk.CommandBuffer, frameIndex: u32) void {
_ = rt;
const shared: *DebugSharedData = &self.sharedData[frameIndex];
var offset: usize = 0;
shared.lock.lock();
defer shared.lock.unlock();
const count: usize = shared.drawsThisFrame.items.len;
if (count == 0)
return;
const mapped = self.gc.vkAllocator.mapBuffer(vk.DrawIndexedIndirectCommand, self.indirectStaging) catch unreachable;
defer self.gc.vkAllocator.unmapMemory(self.indirectStaging);
while (offset < count) : (offset += 1) {
const primitive: DebugPrimitive = shared.drawsThisFrame.items[offset];
var mesh: core.Name = undefined;
switch (primitive.primitive) {
.box => {
mesh = self.meshes[2];
},
.sphere => {
mesh = self.meshes[1];
},
.line => {
mesh = self.meshes[0];
},
}
const indexedMesh = graphics.getIndexedMeshByName(mesh).?;
mapped[offset] = .{
.index_count = indexedMesh.index.size,
.instance_count = 1,
.first_index = indexedMesh.index.start,
.vertex_offset = 0,
.first_instance = @intCast(offset),
};
}
self.uploadIndirectCommands(cmd, @intCast(count));
}
pub fn rtPostDraw(self: *@This(), rt: *graphics.RenderThread, cmd: vk.CommandBuffer, frameIndex: u32) void {
_ = rt;
var zone = tracy.ZoneN(@src(), "Debug draw renderer");
defer zone.End();
const shared: *DebugSharedData = &self.sharedData[frameIndex];
shared.lock.lock();
defer shared.lock.unlock();
var z1 = tracy.ZoneN(@src(), "Debug draw - ssbo upload");
// core.engine_log("count: {d}", .{shared.drawsThisFrame.items.len});
for (shared.drawsThisFrame.items, 0..) |primitive, i| {
const object = &self.mappedBuffers[frameIndex].objects[i];
object.*.color = primitive.color;
object.*.model = primitive.resolve();
}
z1.End();
var vkd = self.gc.vkd;
var z2 = tracy.ZoneN(@src(), "Debug draw - pipeline bind");
vkd.cmdBindPipeline(cmd, .graphics, self.material.pipeline);
var bindOffset: usize = 0;
const count: usize = shared.drawsThisFrame.items.len;
const paddedSceneSize = @as(u32, @intCast(self.gc.pad_uniform_buffer_size(@sizeOf(graphics.NeonVkSceneDataGpu))));
var startOffset: u32 = paddedSceneSize * @as(u32, @intCast(frameIndex));
vkd.cmdBindDescriptorSets(cmd, .graphics, self.material.layout, 0, 1, @ptrCast(&self.gc.frameData[frameIndex].globalDescriptorSet), 1, @ptrCast(&startOffset));
z2.End();
var z3 = tracy.ZoneN(@src(), "Debug draw - render");
var buffers = graphics.getMeshPoolBuffers();
vkd.cmdBindVertexBuffers(cmd, 0, 1, @ptrCast(&buffers.vertex.buffer), @ptrCast(&bindOffset));
vkd.cmdBindIndexBuffer(cmd, buffers.index.buffer, 0, .uint32);
vkd.cmdBindDescriptorSets(cmd, .graphics, self.material.layout, 1, 1, self.pipeData.getDescriptorSet(frameIndex), 0, undefined);
vkd.cmdDrawIndexedIndirect(cmd, self.indirectGpu.buffer, 0, @intCast(count), @sizeOf(vk.DrawIndexedIndirectCommand));
z3.End();
}
// NeonObject Interface Implementation
pub fn init(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.allocator = allocator,
.debugDraws = core.RingQueueU(DebugPrimitive).init(allocator, objectCount) catch unreachable,
};
return self;
}
pub fn shutdown(self: *@This()) void {
for (self.mappedBuffers) |*mapped| {
mapped.unmap(self.gc);
}
self.gc.allocator.free(self.mappedBuffers);
self.gc.vkAllocator.destroyBuffer(&self.indirectGpu);
self.gc.vkAllocator.destroyBuffer(&self.indirectStaging);
self.pipeData.deinit(self.allocator, self.gc);
self.debugDraws.deinit(self.allocator);
for (&self.sharedData) |*shared| {
shared.deinit(self.allocator);
}
core.graphics_logs("shutting down debug draw system");
}
pub fn deinit(self: *@This()) void {
self.shutdown();
self.allocator.destroy(self);
}
};
var gDebugDrawSys: *DebugDrawSubsystem = undefined;
pub fn init_debug_draw_subsystem() !void {
gDebugDrawSys = try core.gEngine.createObject(DebugDrawSubsystem, .{ .can_tick = true });
try gDebugDrawSys.prepareSubsystem(graphics.getContext());
try graphics.registerRendererPlugin(gDebugDrawSys);
try core.installDebugDrawInterface(gDebugDrawSys.allocator, .{
.debugSphereFn = debugSphere,
.debugBoxFn = debugBox,
.debugLineFn = debugLine,
});
}
pub fn shutdown() void {}
const DebugDrawParams = core.DebugDrawParams;
pub fn debugSphere(position: core.Vectorf, radius: f32, params: DebugDrawParams) void {
gDebugDrawSys.debugDraws.push(.{
.primitive = .{ .sphere = .{ .position = position, .radius = radius, .rotation = params.rotation } },
.color = params.color,
.duration = params.duration,
}) catch return;
}
pub fn debugLine(start: core.Vectorf, end: core.Vectorf, params: DebugDrawParams) void {
gDebugDrawSys.debugDraws.push(.{
.primitive = .{ .line = .{
.start = start,
.end = end,
} },
.color = params.color,
.duration = params.duration,
}) catch return;
}
pub fn debugBox(position: core.Vectorf, extents: core.Vectorf, params: DebugDrawParams) void {
gDebugDrawSys.debugDraws.push(.{
.primitive = .{
.box = .{ .position = position, .extents = extents, .rotation = params.rotation },
},
.color = params.color,
.duration = params.duration,
}) catch return;
}

View File

@ -1,307 +0,0 @@
// this folder contains
const std = @import("std");
const root = @import("root");
const bl = root.backlog;
const vk = @import("vulkan");
const core = @import("core");
const graphics = @import("graphics.zig");
const vkinit = graphics.vkinit;
const vma = @import("vma");
const NeonVkContext = graphics.NeonVkContext;
const NeonVkBuffer = graphics.NeonVkBuffer;
const NeonVkAllocator = graphics.NeonVkAllocator;
const ArrayListUnmanaged = std.ArrayListUnmanaged;
// so.. given a single descriptor set:
// 1. create builder
// 2. add buffers for data templates
// 3. finalize and build.
// maybe a better way of doing this:
// NeonGpuObjectBuilder and NeonGpuObject are an abstraction + automation of
// vk.DescriptorSet + vk.Buffer and a way to map them.
// var builder = graphics.NeonGpuObjectBuilder.init(allocator);
// builder.addBuffer(SpriteDataGpu, .objectStorageBuffer);
// builder.addBuffer(CameraDataGpu, .uniform);
// var gpuObject: NeonGpuObject = builder.build();
// TODO: add a way to unmap multiple buffers.. it has been months now. I have no idea what i meant by this.
// ---- Proposed API for implemeting extensions into the game ---
// a GpuPipeData is an API that exists as an API that abstracts both
// vulkan buffer allocation and mapping
pub fn GpuMappingData(comptime ObjectType: type) type {
return struct {
raw: GpuMappingRaw,
objects: []ObjectType, //WARNING! theres a bug do not use this with square operator unless it's a type that's a power of 2
trueObjectSize: usize,
pub fn unmap(self: *@This(), gc: *NeonVkContext) void {
self.raw.unmap(gc);
}
};
}
pub const GpuMappingRaw = struct {
data: []u8,
allocation: vma.Allocation,
pub fn unmap(self: *@This(), gc: *NeonVkContext) void {
gc.vkAllocator.vmaAllocator.unmapMemory(self.allocation);
}
};
pub const GpuPipeDataBinding = struct {
// one slot per frame
buffers: []NeonVkBuffer,
objectCount: usize,
objectSize: usize,
frameCount: usize,
isFrameBuffer: bool = true,
pub fn mapBuffers(self: *@This(), gc: *NeonVkContext, comptime MappingType: type) ![]GpuMappingData(MappingType) {
var frameIndex: usize = 0;
if (self.isFrameBuffer) {
try core.assertf(self.frameCount == self.buffers.len, "mismatched frameBuffer {d} != {d}", .{ self.frameCount, self.buffers.len });
}
// maps buffers for these bindings, one for each frame
var rv = try gc.allocator.alloc(GpuMappingData(MappingType), self.buffers.len);
while (frameIndex < self.buffers.len) : (frameIndex += 1) {
const data = try gc.vkAllocator.vmaAllocator.mapMemory(self.buffers[frameIndex].allocation, MappingType);
var mapping: []MappingType = undefined;
mapping.ptr = @as([*]MappingType, @ptrCast(data));
mapping.len = self.objectCount;
var dataMapping: []u8 = undefined;
dataMapping.ptr = @as([*]u8, @ptrCast(data));
dataMapping.len = self.objectCount;
const gpuMappingData: GpuMappingData(MappingType) = .{
.objects = mapping,
.trueObjectSize = self.objectSize,
.raw = .{ .data = dataMapping, .allocation = self.buffers[frameIndex].allocation },
};
rv[frameIndex] = gpuMappingData;
}
return rv;
}
pub fn deinit(self: *@This(), vkAllocator: *NeonVkAllocator) void {
for (self.buffers) |*buffers| {
buffers.deinit(vkAllocator);
}
}
};
// High level pipe controls for a gpu data pipe
pub const GpuPipeData = struct {
allocator: std.mem.Allocator,
descriptorSetLayout: vk.DescriptorSetLayout,
bindings: []GpuPipeDataBinding,
descriptorSets: []vk.DescriptorSet, // one per frame
descriptorSetLayoutIsAllocated: bool = false,
pub fn getDescriptorSet(self: @This(), frameIndex: usize) [*]const vk.DescriptorSet {
return @as([*]const vk.DescriptorSet, @ptrCast(&self.descriptorSets[frameIndex]));
}
pub fn init(allocator: std.mem.Allocator, bindingCount: usize, frameCount: usize) !@This() {
const self = GpuPipeData{
.descriptorSetLayout = undefined,
.bindings = try allocator.alloc(GpuPipeDataBinding, bindingCount),
.descriptorSets = try allocator.alloc(vk.DescriptorSet, frameCount),
.allocator = allocator,
};
for (self.bindings) |*binding| {
binding.buffers = try allocator.alloc(NeonVkBuffer, frameCount);
binding.frameCount = frameCount;
}
return self;
}
// Maps each buffer per frame
pub fn mapBuffers(self: *@This(), gc: *NeonVkContext, comptime ObjectType: type, binding: usize) ![]GpuMappingData(ObjectType) {
var pipeDataBuffer = self.bindings[binding];
return pipeDataBuffer.mapBuffers(gc, ObjectType);
}
// pub fn unmapAll(self: *@This(), mappings: anytype);
pub fn deinit(self: *@This(), allocator: std.mem.Allocator, gc: *NeonVkContext) void {
if (self.descriptorSetLayoutIsAllocated) {
gc.vkd.destroyDescriptorSetLayout(gc.dev, self.descriptorSetLayout, null);
}
for (self.bindings) |*binding| {
binding.deinit(gc.vkAllocator);
allocator.free(binding.buffers);
}
allocator.free(self.descriptorSets);
allocator.free(self.bindings);
}
};
pub const BindingMode = enum { uniform, storageBuffer };
pub const GpuPipeDataBuilder = struct {
const BindingObjectInfo = struct {
objectCount: usize,
finalObjectSize: usize,
trueObjectSize: usize,
bindingMode: BindingMode,
};
gc: *NeonVkContext,
allocator: std.mem.Allocator,
currentBinding: u32 = 0,
frameCount: usize = graphics.constants.NUM_FRAMES,
objectCount: usize = graphics.constants.MAX_OBJECTS,
bindings: ArrayListUnmanaged(vk.DescriptorSetLayoutBinding) = .{},
bindingObjectInfos: ArrayListUnmanaged(BindingObjectInfo) = .{},
pub fn init(allocator: std.mem.Allocator, gc: *NeonVkContext) @This() {
const self = GpuPipeDataBuilder{
.allocator = allocator,
.gc = gc,
};
return self;
}
pub fn setObjectCount(
self: *@This(),
count: usize,
) void {
self.objectCount = count;
}
pub fn addBufferBinding(
self: *@This(),
comptime BindingType: type,
descriptorType: vk.DescriptorType,
stageFlags: vk.ShaderStageFlags,
bindingMode: BindingMode,
) !void {
var gc = self.gc;
const binding = vkinit.descriptorSetLayoutBinding(descriptorType, stageFlags, self.currentBinding);
// core.graphics_log("builder adding additional binding {any} {any} objectSize = {d}", .{ descriptorType, stageFlags, @sizeOf(BindingType) });
try self.bindings.append(self.allocator, binding);
var objCount: usize = 1;
// todo: there is a bug here because this code is incomplete this only accounts for storage buffers and uniforms
if (descriptorType == .storage_buffer) {
objCount = self.objectCount;
}
var bindingObjectInfo: BindingObjectInfo = .{
.objectCount = objCount,
.finalObjectSize = @sizeOf(BindingType),
.trueObjectSize = @sizeOf(BindingType),
.bindingMode = bindingMode,
};
// uniforms require that the buffer object gets padded to the correct size.
if (descriptorType != .storage_buffer) {
bindingObjectInfo.finalObjectSize = gc.pad_uniform_buffer_size(bindingObjectInfo.finalObjectSize);
// core.engine_log("final object size has been padded: {d}", .{bindingObjectInfo.finalObjectSize});
} else {
var trueSize: usize = 1;
while (trueSize < bindingObjectInfo.finalObjectSize) {
trueSize *= 2;
}
bindingObjectInfo.finalObjectSize = trueSize;
// core.engine_log("final object size has been padded as storage: {d}", .{bindingObjectInfo.finalObjectSize});
}
try self.bindingObjectInfos.append(self.allocator, bindingObjectInfo);
self.currentBinding += 1;
}
pub fn build(self: *@This(), comptime buildName: []const u8) !GpuPipeData {
var rv = try GpuPipeData.init(self.allocator, self.bindings.items.len, self.frameCount);
var gc: *NeonVkContext = self.gc;
var setInfo = vk.DescriptorSetLayoutCreateInfo{ .binding_count = @as(u32, @intCast(self.bindings.items.len)), .flags = .{}, .p_bindings = self.bindings.items.ptr };
rv.descriptorSetLayout = try gc.vkd.createDescriptorSetLayout(gc.dev, &setInfo, null);
rv.descriptorSetLayoutIsAllocated = true;
// core.graphics_log("finalizing build creating descriptor set layout at 0x{x} buildName: {s}", .{ @intFromEnum(rv.descriptorSetLayout), buildName });
for (rv.descriptorSets, 0..) |_, frameId| {
var descriptorAllocInfo = vk.DescriptorSetAllocateInfo{
.descriptor_pool = gc.descriptorPool,
.descriptor_set_count = 1,
.p_set_layouts = @ptrCast(&rv.descriptorSetLayout),
};
try gc.vkd.allocateDescriptorSets(gc.dev, &descriptorAllocInfo, @as([*]vk.DescriptorSet, @ptrCast(&rv.descriptorSets[frameId])));
}
var bindingId: usize = 0;
while (bindingId < self.bindings.items.len) : (bindingId += 1) {
const binding = &rv.bindings[bindingId];
const bindingInfo: BindingObjectInfo = self.bindingObjectInfos.items[bindingId];
// core.graphics_log("allocating {d} frame buffers for binding {d} buffer size = {d} object size = {d}", .{ binding.buffers.len, bindingId, bindingInfo.finalObjectSize * bindingInfo.objectCount, bindingInfo.finalObjectSize });
for (binding.buffers, 0..) |*buffer, frameId| {
var usageFlags: vk.BufferUsageFlags = .{};
var memoryFlags: vma.MemoryUsage = .unknown;
var descriptorType: vk.DescriptorType = .sampler;
switch (bindingInfo.bindingMode) {
.uniform => {
usageFlags.uniform_buffer_bit = true;
memoryFlags = .cpuToGpu;
descriptorType = .uniform_buffer;
},
.storageBuffer => {
usageFlags.storage_buffer_bit = true;
memoryFlags = .cpuToGpu;
descriptorType = .storage_buffer;
},
}
buffer.* = try gc.create_buffer(
bindingInfo.finalObjectSize * bindingInfo.objectCount,
usageFlags,
memoryFlags,
"GPU binding buffer creation " ++ @src().fn_name ++ ": " ++ buildName,
);
var bufferInfo = vk.DescriptorBufferInfo{
.buffer = buffer.buffer,
.offset = 0,
.range = bindingInfo.finalObjectSize * bindingInfo.objectCount,
};
var descriptorWrite = vkinit.writeDescriptorSet(
descriptorType,
rv.descriptorSets[frameId],
&bufferInfo,
@as(u32, @intCast(bindingId)),
);
gc.vkd.updateDescriptorSets(gc.dev, 1, @ptrCast(&descriptorWrite), 0, undefined);
}
}
return rv;
}
pub fn deinit(self: *@This()) void {
self.bindings.deinit(self.allocator);
self.bindingObjectInfos.deinit(self.allocator);
}
};

View File

@ -1,196 +0,0 @@
const core = @import("core");
const assets = @import("assets");
const std = @import("std");
const memory = core.MemoryTracker;
pub const ozz = @import("ozz");
const texture_cooking = @import("cooking/texture_cooking.zig");
const mesh_cooking = @import("cooking/mesh_cooking.zig");
pub const vk_renderer = @import("vk_renderer.zig");
const materials = @import("materials.zig");
pub usingnamespace @import("debug_draws.zig");
pub const gpu_pipe_data = @import("gpu_pipe_data.zig");
pub const BoneHandle = animation_system.BoneHandle;
pub const SkyboxSystem = @import("skybox.zig");
pub const setSkybox = SkyboxSystem.setSkybox;
pub const animation_system = @import("animation/animationSystem.zig");
pub const AnimationSystem = animation_system.AnimationSystem;
pub const Animator = animation_system.Animator;
pub const AnimationTrack = animation_system.AnimationTrack;
pub const Skeleton = animation_system.Skeleton;
pub const animation_resolver = @import("animation/animResolver.zig");
pub const AnimResolverRef = animation_resolver.AnimResolverRef;
pub const AnimResolverInterface = animation_resolver.AnimResolverInterface;
pub const SingleAnimationResolver = animation_resolver.SingleAnimationResolver;
pub const BlenderList = animation_resolver.BlenderList;
pub const AnimSampler = animation_resolver.AnimSampler;
const vk_cubemap = @import("vk_renderer/vk_cubemap.zig");
pub const CubeMapDirs = vk_cubemap.CubeMapDirs;
pub const MakeCubeMapList = vk_cubemap.MakeCubeMapList;
pub const animation_loaders = @import("animation/loaders.zig");
pub const RenderThread = @import("vk_renderer/RenderThread.zig");
pub const vkinit = @import("vk_init.zig");
pub const vk_allocator = @import("vk_allocator.zig");
pub const NeonVkAllocator = vk_allocator.NeonVkAllocator;
pub const NeonVkPipelineBuilder = vk_renderer.NeonVkPipelineBuilder;
pub const NeonVkContext = vk_renderer.NeonVkContext;
pub const constants = @import("vk_constants.zig");
pub const NeonVkImage = vk_renderer.NeonVkImage;
pub const Material = materials.Material;
pub const RendererInterfaceRef = vk_renderer.RendererInterfaceRef;
pub const RendererInterface = vk_renderer.RendererInterface;
pub const texture = @import("texture.zig");
pub const debug_draw = @import("debug_draws.zig");
pub const mesh = @import("mesh.zig");
pub const Mesh = mesh.Mesh;
pub const DynamicMesh = mesh.DynamicMesh;
pub const IndexBuffer = mesh.IndexBuffer;
pub const Texture = texture.Texture;
pub const MeshVertex = mesh.MeshVertex;
pub const mesh_pool = @import("vk_renderer/vk_mesh_pool.zig");
pub const MeshSourceType = mesh_pool.MeshSourceType;
pub const loadIndexedMeshForPooling = mesh_pool.loadIndexedMeshForPooling;
pub const getMeshPoolBuffers = mesh_pool.getMeshPoolBuffers;
pub const getIndexedMeshByName = mesh_pool.getIndexedMeshByName;
// pub const DynamicTexture = @import("dynamic_texture/DynamicTexture.zig");
pub const vk_util = @import("vk_utils.zig");
pub const createAndInstallTextureFromPixels = vk_util.createAndInstallTextureFromPixels;
const vk_api = @import("../vk_api.zig");
pub const vkd = &vk_api.vkd;
pub const vki = &vk_api.vki;
pub const vkb = &vk_api.vkb;
pub const PixelBufferRGBA8 = @import("PixelBufferRGBA8.zig");
pub const vk_assetLoaders = @import("vk_assetLoaders.zig");
pub const PixelPos = vk_renderer.PixelPos;
pub const NeonVkBuffer = vk_renderer.NeonVkBuffer;
pub const NumFrames = constants.NUM_FRAMES;
const engine_logs = core.engine_logs;
const engine_log = core.engine_log;
pub fn getContext() *NeonVkContext {
return vk_renderer.gContext;
}
pub usingnamespace @import("vk_renderer/vk_renderer_types.zig");
pub const render_objects = @import("render_objects.zig");
pub const Camera = render_objects.Camera;
pub const StaticMesh = render_objects.StaticMesh;
pub const IndexedMesh = mesh_pool.IndexedMesh;
pub fn registerRendererPlugin(value: anytype) !void {
const ref = RendererInterfaceRef{
.ptr = value,
.vtable = &@TypeOf(value.*).RendererInterfaceVTable,
};
var gc = getContext();
try gc.rendererPlugins.append(gc.allocator, ref);
}
var gCooking: bool = false;
const primitives = [_]assets.AssetImportReference{
assets.MakeImportRef("Mesh", "m_primitive_sphere", "meshes/primitive_sphere.obj"),
assets.MakeImportRef("Mesh", "m_primitive_box", "meshes/primitive_box.obj"),
assets.MakeImportRef("Mesh", "m_primitive_line", "meshes/primitive_line.obj"),
assets.MakeImportRef("Mesh", "m_skybox", "meshes/skybox.obj"),
};
pub fn start_module(comptime programSpec: anytype, args: anytype, allocator: std.mem.Allocator) !void {
_ = args;
if (!core.isUtility()) {
engine_logs("graphics module starting up...");
const context: *NeonVkContext = core.gEngine.createObject(
NeonVkContext,
.{ .can_tick = true, .isCore = true },
) catch unreachable;
vk_renderer.gContext = context;
const as = try core.createObject(AnimationSystem, .{ .can_tick = false });
try animation_loaders.initLoaders();
try registerRendererPlugin(as);
vk_assetLoaders.init_loaders(allocator) catch unreachable;
try assets.loadList(primitives);
debug_draw.init_debug_draw_subsystem() catch unreachable;
context.skybox = SkyboxSystem.create(context.allocator) catch return core.EngineDataEventError.UnknownStatePanic;
}
if (@hasField(@TypeOf(programSpec), "cooking")) {
gCooking = true;
try texture_cooking.initCooker(allocator);
try mesh_cooking.initCooker(allocator);
}
}
pub fn shutdown_module(allocator: std.mem.Allocator) void {
_ = allocator;
if (gCooking) {
mesh_cooking.deinitCooker();
texture_cooking.deinitCooker();
}
if (!core.isUtility()) {
engine_logs("graphics module shutting down...");
vk_renderer.gContext.shutdown();
}
}
pub var icon: []const u8 = "content/textures/icon.png";
pub fn setStartupSettings(comptime field: []const u8, value: anytype) void {
@field(vk_renderer.gGraphicsStartupSettings, field) = value;
}
pub fn getStartupSettings() *const @TypeOf(vk_renderer.gGraphicsStartupSettings) {
return &vk_renderer.gGraphicsStartupSettings;
}
pub fn loadSpv(allocator: std.mem.Allocator, path: []const u8) ![]const u32 {
core.engine_log("loading path {s}", .{path});
const search_prefixes: []const []const u8 = &.{
"zig-out/shaders",
"shaders",
};
var s_path: [4096]u8 = undefined;
for (search_prefixes) |prefix| {
const s = try std.fmt.bufPrint(&s_path, "{s}/{s}", .{ prefix, path });
var file = std.fs.cwd().openFile(s, .{ .mode = .read_only }) catch continue;
const filesize = (try file.stat()).size;
const buffer: []u8 = try allocator.alignedAlloc(u8, 4, filesize);
try file.reader().readNoEof(buffer);
var rv: []u32 = undefined;
rv.ptr = @as([*]u32, @ptrCast(@alignCast(buffer.ptr)));
rv.len = buffer.len / 4;
return rv;
}
return error.FileNotFound;
}
pub const Module = core.ModuleDescription{
.name = "graphics",
.enabledByDefault = true,
};

View File

@ -1,52 +0,0 @@
const std = @import("std");
const vk = @import("vulkan");
const core = @import("core");
const VkConstants = @import("vk_constants.zig");
const meshes = @import("mesh.zig");
const NeonVkContext = @import("vk_renderer.zig").NeonVkContext;
const vk_pipeline = @import("vk_pipeline.zig");
const NeonVkPipelineBuilder = vk_pipeline.NeonVkPipelineBuilder;
const EulerAngles = core.EulerAngles;
const Mat = core.Mat;
const Vectorf = core.Vectorf;
const Quat = core.Quat;
const zm = core.zm;
const mul = zm.mul;
pub const Material = struct {
materialName: core.Name,
textureSet: vk.DescriptorSet = .null_handle,
pipeline: vk.Pipeline,
layout: vk.PipelineLayout,
pub fn deinit(self: *Material, ctx: *NeonVkContext) void {
ctx.vkd.destroyPipeline(ctx.dev, self.pipeline, null);
ctx.vkAllocator.destroyPipelineLayout(ctx.dev, self.layout);
}
};
pub const MaterialBuilder = struct {
const Self = @This();
allocator: std.mem.Allocator,
ctx: *NeonVkContext,
pipelineBuilder: NeonVkPipelineBuilder,
pub fn init(ctx: *NeonVkContext) MaterialBuilder {
const self = MaterialBuilder{
.allocator = ctx.allocator,
.ctx = ctx,
};
return self;
}
pub fn build(self: *Self) !void {
_ = self;
// try self.ctx.add_material();
}
pub fn deinit(self: *Self) void {
_ = self;
}
};

View File

@ -1,303 +0,0 @@
const std = @import("std");
const core = @import("core");
const vk_renderer = @import("vk_renderer.zig");
const vma = @import("vma");
const vk = @import("vulkan");
const obj_loader = @import("objLoader");
const constants = @import("vk_constants.zig");
const vk_utils = @import("vk_utils.zig");
const vk_dynamic_mesh = @import("vk_dynamic_mesh.zig");
const NeonVkUploader = vk_utils.NeonVkUploader;
const NeonVkBuffer = vk_renderer.NeonVkBuffer;
const ObjMesh = obj_loader.ObjMesh;
const ArrayList = std.ArrayList;
const Vectorf = core.Vectorf;
const Vector2f = core.Vector2f;
const Color = core.colors.Color;
const NeonVkContext = vk_renderer.NeonVkContext;
const debug_struct = core.debug_struct;
pub const DynamicMesh = vk_dynamic_mesh.DynamicMesh;
pub const DynamicMeshManager = vk_dynamic_mesh.DynamicMeshManager;
pub const MeshVertex = extern struct {
position: Vectorf = .{},
normal: Vectorf = .{},
color: Color = .{},
uv: Vector2f = .{},
bones: [4]u8 = .{ 0, 0, 0, 0 },
weights: [4]u8 = .{ 0, 0, 0, 0 },
};
pub const IndexBuffer = struct {
buffer: NeonVkBuffer,
indices: []const u32,
allocator: std.mem.Allocator,
pub fn uploadIndexBuffer(gc: *NeonVkContext, indices: []const u32, allocator: std.mem.Allocator) !@This() {
var self = @This(){
.buffer = undefined,
.indices = indices,
.allocator = allocator,
};
self.indices = try allocator.dupe(u32, indices);
const bufferSize = indices.len * @sizeOf(u32);
const bci = vk.BufferCreateInfo{
.flags = .{},
.size = bufferSize,
.usage = .{ .transfer_src_bit = true },
.sharing_mode = .exclusive,
.queue_family_index_count = 0,
.p_queue_family_indices = undefined,
};
const vmaCreateInfo = vma.AllocationCreateInfo{
.flags = .{},
.usage = .cpuOnly,
};
var stagingBuffer = try gc.vkAllocator.createBuffer(bci, vmaCreateInfo, @src().fn_name ++ " - upload buffer");
defer stagingBuffer.deinit(gc.vkAllocator);
{
const data = try gc.vkAllocator.vmaAllocator.mapMemory(stagingBuffer.allocation, u8);
var dataSlice: []u8 = undefined;
dataSlice.ptr = data;
dataSlice.len = bufferSize;
var iSlice: []const u8 = undefined;
iSlice.ptr = @as([*]const u8, @ptrCast(indices.ptr));
iSlice.len = bufferSize;
@memcpy(dataSlice, iSlice);
gc.vkAllocator.vmaAllocator.unmapMemory(stagingBuffer.allocation);
}
// GPU sided buffer
const gpuBci = vk.BufferCreateInfo{
.flags = .{},
.size = bufferSize,
.usage = .{ .transfer_dst_bit = true, .index_buffer_bit = true },
.sharing_mode = .exclusive,
.queue_family_index_count = 0,
.p_queue_family_indices = undefined,
};
const gpuVmaCreateInfo = vma.AllocationCreateInfo{
.flags = .{},
.usage = .gpuOnly,
};
self.buffer = try gc.vkAllocator.createBuffer(gpuBci, gpuVmaCreateInfo, @src().fn_name ++ " - gpu buffer");
//try gc.start_upload_context(&gc.uploadContext);
try gc.uploader.startUploadContext();
{
var copy = vk.BufferCopy{
.dst_offset = 0,
.src_offset = 0,
.size = bufferSize,
};
const cmd = gc.uploader.commandBuffer;
// core.graphics_log("Starting command copy buffer", .{});
gc.vkd.cmdCopyBuffer(
cmd,
stagingBuffer.buffer,
self.buffer.buffer,
1,
@as([*]const vk.BufferCopy, @ptrCast(&copy)),
);
}
//try gc.finish_upload_context(&gc.uploadContext);
try gc.uploader.finishUploadContext();
return self;
}
pub fn deinit(self: *@This(), gc: *NeonVkContext) void {
self.buffer.deinit(gc.vkAllocator);
self.allocator.free(self.indices);
}
};
pub fn loadObjMeshVertices(vertices: *ArrayList(MeshVertex), mesh: ObjMesh) !void {
try mesh.validate_mesh();
try vertices.ensureTotalCapacity(mesh.v_faces.items.len * 3);
for (mesh.v_faces.items) |face| {
if (face.count == 3) {
var i: u32 = 0;
while (i < 3) : (i += 1) {
const v = vertexFromFaceOffset(mesh, face, i);
try vertices.append(v);
}
} else if (face.count == 4) {
const vx = [_]MeshVertex{
vertexFromFaceOffset(mesh, face, 0),
vertexFromFaceOffset(mesh, face, 1),
vertexFromFaceOffset(mesh, face, 2),
vertexFromFaceOffset(mesh, face, 2),
vertexFromFaceOffset(mesh, face, 3),
vertexFromFaceOffset(mesh, face, 0),
};
try vertices.appendSlice(vx[0..]);
}
}
}
fn vertexFromFaceOffset(mesh: ObjMesh, face: obj_loader.ObjFace, offset: u32) MeshVertex {
const p = mesh.v_positions.items[face.vertex[offset] - 1];
const n = mesh.v_normals.items[face.normal[offset] - 1];
const u = mesh.v_uvs.items[face.texture[offset] - 1];
const v = MeshVertex{
.position = .{ .x = p.x, .y = p.y, .z = p.z },
.normal = .{ .x = n.x, .y = n.y, .z = n.z },
.color = .{ .r = n.x, .g = n.y, .b = n.z, .a = 1.0 },
.uv = .{ .x = u.x, .y = 1 - u.y },
};
return v;
}
// legacy, don't use
pub const Mesh = struct {
vertices: ArrayList(MeshVertex),
buffer: NeonVkBuffer,
allocator: std.mem.Allocator,
pub fn init(context: *NeonVkContext, allocator: std.mem.Allocator) Mesh {
const self = Mesh{
.vertices = ArrayList(MeshVertex).init(allocator),
.buffer = undefined,
.allocator = allocator,
};
_ = context;
return self;
}
pub fn upload(self: *Mesh, ctx: *NeonVkContext) !void {
try ctx.stage_and_push_mesh(self);
}
pub fn loadFromObjFileCooked(self: *Mesh, fileName: []const u8) !void {
const mapping = try core.fs().loadFile(fileName);
defer core.fs().unmap(mapping);
const s = @sizeOf(MeshVertex);
var i: usize = 0;
var vertexOffset: usize = 0;
try self.vertices.resize(1 + mapping.bytes.len / s);
while (i < mapping.bytes.len) : (i += s) {
self.vertices.items[vertexOffset] = @as(*const MeshVertex, @ptrCast(@alignCast(mapping.bytes.ptr + i))).*;
vertexOffset += 1;
}
}
pub fn load_from_obj_file(self: *Mesh, fileName: []const u8) !void {
const mapping = try core.fs().loadFile(fileName);
defer core.fs().unmap(mapping);
var fileObjs = try obj_loader.loadObjBytes(mapping.bytes, self.allocator);
defer fileObjs.deinit();
if (fileObjs.meshes.items.len > 0) {
// default grabbing shape zero
core.graphics_log("loading mesh: {s}", .{fileName});
// fileObjs.meshes.items[0].print_stats();
// try self.load_from_obj_mesh(fileObjs.meshes.items[0]);
try loadObjMeshVertices(&self.vertices, fileObjs.meshes.items[0]);
}
core.graphics_log("mesh loaded with {d} vertices size {d}", .{ self.vertices.items.len, self.vertices.items.len * @sizeOf(MeshVertex) });
}
pub fn deinit(self: *Mesh, ctx: *NeonVkContext) void {
self.buffer.deinit(ctx.vkAllocator);
self.vertices.deinit();
}
};
pub const VertexInputDescription = struct {
bindings: ArrayList(vk.VertexInputBindingDescription),
attributes: ArrayList(vk.VertexInputAttributeDescription),
flags: vk.PipelineVertexInputStateCreateFlags = .{},
pub fn init(allocator: std.mem.Allocator) !VertexInputDescription {
var self = VertexInputDescription{
.bindings = ArrayList(vk.VertexInputBindingDescription).init(allocator),
.attributes = ArrayList(vk.VertexInputAttributeDescription).init(allocator),
};
try self.bindings.append(.{
.binding = 0,
.stride = @sizeOf(MeshVertex),
.input_rate = .vertex,
});
//debug_struct("bindings 0", self.bindings.items[0]);
// position
try self.attributes.append(.{
.binding = 0,
.location = 0,
.format = .r32g32b32_sfloat,
.offset = @offsetOf(MeshVertex, "position"),
});
//debug_struct("attributes 0", self.attributes.items[0]);
// normal
try self.attributes.append(.{
.binding = 0,
.location = 1,
.format = .r32g32b32_sfloat,
.offset = @offsetOf(MeshVertex, "normal"),
});
//debug_struct("attributes 0", self.attributes.items[1]);
// color
try self.attributes.append(.{
.binding = 0,
.location = 2,
.format = .r32g32b32a32_sfloat,
.offset = @offsetOf(MeshVertex, "color"),
});
//debug_struct("attributes 0", self.attributes.items[2]);
try self.attributes.append(.{
.binding = 0,
.location = 3,
.format = .r32g32_sfloat,
.offset = @offsetOf(MeshVertex, "uv"),
});
try self.attributes.append(.{
.binding = 0,
.location = 4,
.format = .a8b8g8r8_uint_pack32,
.offset = @offsetOf(MeshVertex, "bones"),
});
try self.attributes.append(.{
.binding = 0,
.location = 5,
.format = .a8b8g8r8_uint_pack32,
.offset = @offsetOf(MeshVertex, "weights"),
});
return self;
}
pub fn deinit(self: *@This()) void {
self.bindings.deinit();
self.attributes.deinit();
}
};

View File

@ -1,316 +0,0 @@
const std = @import("std");
const vk = @import("vulkan");
const resources = @import("resources");
const core = @import("core");
const VkConstants = @import("vk_constants.zig");
const graphics = @import("graphics.zig");
const meshes = @import("mesh.zig");
const NeonVkContext = @import("vk_renderer.zig").NeonVkContext;
const materials = @import("materials.zig");
const animationSystem = @import("animation/animationSystem.zig");
const Animator = animationSystem.Animator;
const Material = materials.Material;
const EulerAngles = core.EulerAngles;
const Mat = core.Mat;
const Vectorf = core.Vectorf;
const Quat = core.Quat;
const zm = core.zm;
const mul = zm.mul;
const Mesh = meshes.Mesh;
const mesh_pool = @import("vk_renderer/vk_mesh_pool.zig");
const IndexedMesh = mesh_pool.IndexedMesh;
// lol we need to rename this thing again, it should be called RenderMesh
pub const StaticMeshSet = core.SparseSet(StaticMesh);
pub const StaticMesh = struct {
const Self = @This();
mesh: ?IndexedMesh = null,
// texture: ?vk.DescriptorSet = null,
textureId: ?u32 = null,
transform: core.Mat = core.zm.translation(0, 0, 0),
visibility: bool = true,
// new position and rotator based api
position: Vectorf = .{},
rotation: Quat = .{ 0, 0, 0, 1 },
scale: Vectorf = .{ .x = 1, .y = 1, .z = 1 },
textureName: core.Name = core.NameInvalid,
meshName: core.Name = core.NameInvalid,
animated: bool = false, // todo remove
animator: ?*Animator = null,
flags: Flags0 = .{},
pub var BaseContainer: *StaticMeshSet = undefined;
pub const ComponentName = "StaticMesh";
pub const ScriptExports: []const []const u8 = &.{
"applyRelativeRotationX",
"applyRelativeRotationY",
"applyRelativeRotationZ",
"setMesh",
"setTextureByName",
};
pub const Flags0 = packed struct(u32) {
alwaysInFront: bool = false,
useAltFov: bool = false,
_pad: u30 = 0,
};
pub fn setMeshByName(self: *@This(), meshName: core.Name) void {
self.meshName = meshName;
}
// script function
pub fn setMesh(self: *@This(), meshName: []const u8) void {
const name = core.MakeName(meshName);
self.mesh = graphics.getIndexedMeshByName(core.MakeName(meshName));
self.meshName = name;
}
pub fn fromTransform(transform: core.Mat) Self {
var self = Self{
.mesh = null,
.transform = transform,
.position = undefined,
.rotation = undefined,
.scale = undefined,
};
self.updateScalars();
return self;
}
pub fn setTexture(self: *Self, textureName: []const u8) void {
var name = core.MakeName(textureName);
self.textureId = graphics.getContext().textureIds.get(name.handle());
self.textureName = name;
}
pub fn setTextureByName(self: *Self, _name: core.Name) void {
var name = _name;
self.textureId = graphics.getContext().textureIds.get(name.handle());
self.textureName = name;
}
pub fn updateTexture(self: *@This(), gc: *NeonVkContext) void {
self.textureId = gc.textureIds.get(self.textureName.handle());
}
pub fn applyTransform(self: *StaticMesh, transform: core.Mat) void {
self.transform = core.zm.mul(self.transform, transform);
self.updateScalars();
}
pub fn applyRelativeRotationX(self: *StaticMesh, angle: f32) void {
var imat = core.zm.identity();
imat[0][3] = -self.transform[0][3];
imat[1][3] = -self.transform[1][3];
imat[2][3] = -self.transform[2][3];
const rmat = core.zm.identity();
imat[0][3] = self.transform[0][3];
imat[1][3] = self.transform[1][3];
imat[2][3] = self.transform[2][3];
var newTransform = core.zm.mul(imat, self.transform);
newTransform = core.zm.mul(core.zm.rotationX(angle), newTransform);
newTransform = core.zm.mul(rmat, newTransform);
self.transform = newTransform;
}
pub fn applyRelativeRotationZ(self: *StaticMesh, angle: f32) void {
var imat = core.zm.identity();
imat[0][3] = -self.transform[0][3];
imat[1][3] = -self.transform[1][3];
imat[2][3] = -self.transform[2][3];
const rmat = core.zm.identity();
imat[0][3] = self.transform[0][3];
imat[1][3] = self.transform[1][3];
imat[2][3] = self.transform[2][3];
var newTransform = core.zm.mul(imat, self.transform);
newTransform = core.zm.mul(core.zm.rotationZ(angle), newTransform);
newTransform = core.zm.mul(rmat, newTransform);
self.transform = newTransform;
}
pub fn applyRelativeRotationY(self: *StaticMesh, angle: f32) void {
var imat = core.zm.identity();
imat[0][3] = -self.transform[0][3];
imat[1][3] = -self.transform[1][3];
imat[2][3] = -self.transform[2][3];
const rmat = core.zm.identity();
imat[0][3] = self.transform[0][3];
imat[1][3] = self.transform[1][3];
imat[2][3] = self.transform[2][3];
var newTransform = core.zm.mul(imat, self.transform);
newTransform = core.zm.mul(core.zm.rotationY(angle), newTransform);
newTransform = core.zm.mul(rmat, newTransform);
self.transform = newTransform;
}
pub fn updateScalars(self: *StaticMesh) void {
self.position = Vectorf.fromZm(mul(self.transform, Vectorf.new(0.0, 0.0, 0.0).toZm()));
self.rotation = zm.matToQuat(self.transform);
self.scale = core.matToScalef(self.transform);
}
pub fn applyScalars(self: *StaticMesh) void {
var newTransform = core.zm.mul(
core.zm.scalingV(self.scale.toZm()),
core.zm.matFromQuat(self.rotation),
);
newTransform = core.zm.mul(
newTransform,
core.zm.translationV(self.position.toZm()),
);
self.transform = newTransform;
}
pub fn initECS(self: *@This(), handle: core.ObjectHandle) void {
const entity = core.Entity.fromHandle(handle);
if (entity.get(core.Scene)) |scene| {
self.position = scene.getPosition();
self.rotation = scene.getRotation().quat;
self.scale = scene.getScaleV();
} else {
_ = entity.addComponent(core.Scene);
}
}
};
fn makePerspective(fov: f32, aspect: f32, near: f32, far: f32) Mat {
const proj = core.zm.perspectiveFovRh(
core.radians(fov),
aspect,
near,
far,
);
// proj[1][1] *= -1;
return proj;
}
// Camera coordinate system:
//
// from you as a user, staring at the screen:
//
// This is a right handed coordinate system
//
// forward = +Z (index finger)
// left = +X (middle finger)
// up = +Y (thumb)
const ecs = core.ecs;
pub const Camera = struct {
fov: f32 = 70.0,
altFov: f32 = 70.0,
aspect: f32 = 16.0 / 9.0,
near_clipping: f32 = 0.1,
far_clipping: f32 = 10000.0,
position: Vectorf = Vectorf{ .x = 0.0, .y = 0.0, .z = 0.0 },
rotation: Quat, // todo, remove, we only work with euler tracks now for camera.
// applied in that order,
yaw: f32 = 0,
pitch: f32 = 0,
roll: f32 = 0,
transform: core.Transform = zm.identity(),
worldTransform: Mat = zm.identity(),
projection: Mat = makePerspective(
core.radians(70.0), // angle
16.0 / 9.0,
0.1,
200000,
),
projectionAlt: Mat = makePerspective(
core.radians(70.0), // angle
16.0 / 9.0,
0.0001,
1000,
),
final: Mat = zm.identity(),
finalAlt: Mat = zm.identity(),
pub const EcsComponentDefinition = ecs.DefineComponent(@This(), .set); // set, map, multiset, maplist
pub fn init() Camera {
return .{
.rotation = zm.quatFromRollPitchYaw(0.0, 0.0, 0.0),
};
}
pub fn translate(self: *Camera, offset: core.Vectorf) void {
var off: core.Vectorf = offset;
off.y = offset.y;
off.x = offset.x;
off.z = offset.z;
self.*.position = self.position.add(off);
}
pub fn getRotation(self: *Camera) Quat {
return zm.quatFromMat(self.transform);
}
pub fn setRotationEuler(self: *@This(), x: f32, y: f32, z: f32) void {
self.rotation = core.zm.quatFromRollPitchYaw(x, y + core.radians(180.0), z);
}
pub fn updateCamera(self: *Camera) void {
self.projection = zm.perspectiveFovRh(core.radians(self.fov), 16.0 / 9.0, 0.1, 200000);
self.projection[1][1] *= -1;
self.projectionAlt = zm.perspectiveFovRh(core.radians(self.altFov), 16.0 / 9.0, 0.01, 200000);
self.projectionAlt[1][1] *= -1;
// self.projectionAlt = self.projection;
}
pub fn resolve(self: *Camera) void {
{
var base = core.zm.identity();
base = mul(core.zm.rotationY(-self.yaw), base);
base = mul(core.zm.rotationX(self.pitch), base);
base = mul(core.zm.rotationZ(self.roll), base);
const pr2 = core.scene.SceneObjectPosRot{
.position = self.position,
};
self.worldTransform = mul(base, pr2.toTransform());
}
// calculate viewProjections
{
var base = core.zm.rotationY(self.yaw + core.radians(180.0));
base = mul(base, core.zm.rotationX(self.pitch));
base = mul(base, core.zm.rotationZ(self.roll));
self.transform = base;
// self.transform = mul(
// base,
// mul(zm.matFromQuat(self.rotation), zm.rotationY(core.radians(180.0))),
// );
var position = self.position;
// position.x *= -1;
// position.z *= -1;
self.transform = mul(zm.translationV(position.fmul(-1).toZm()), self.transform);
self.final = mul(self.transform, self.projection);
self.finalAlt = mul(self.transform, self.projectionAlt);
}
}
};

View File

@ -1,96 +0,0 @@
allocator: std.mem.Allocator,
cubeMapShared: [graphics.NumFrames]?vk.DescriptorSet = .{ null, null },
cubeMapTextureSet: ?vk.DescriptorSet = null,
cubeMapName: ?core.Name = null,
material: *graphics.Material = undefined,
mesh: ?graphics.IndexedMesh = null,
pub const RendererInterfaceVTable = graphics.RendererInterface.from(@This());
pub var gSkybox: *@This() = undefined;
pub fn create(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.allocator = allocator,
};
gSkybox = self;
try self.initPipeline();
try graphics.registerRendererPlugin(self);
return self;
}
pub fn sendShared(self: *@This(), fi: u32) void {
if (self.cubeMapName == null) {
self.cubeMapShared[fi] = self.cubeMapTextureSet;
return;
} else {
if (self.mesh == null)
self.mesh = graphics.getIndexedMeshByName(core.MakeName("m_skybox"));
if (self.cubeMapTextureSet == null) {
const handle = self.cubeMapName.?.handle();
self.cubeMapTextureSet = graphics.getContext().textureSets.get(handle);
}
}
self.cubeMapShared[fi] = self.cubeMapTextureSet;
}
pub fn initPipeline(self: *@This()) !void {
const gc = graphics.getContext();
var pipelineBuilder = try graphics.NeonVkPipelineBuilder.init(
gc.dev,
gc.vkd,
self.allocator,
gc.vkAllocator,
skybox_vert.spv(),
skybox_frag.spv(),
);
defer pipelineBuilder.deinit();
try pipelineBuilder.add_mesh_description();
try pipelineBuilder.add_layout(gc.globalDescriptorLayout);
try pipelineBuilder.add_layout(gc.singleTextureSetLayout);
try pipelineBuilder.add_depth_stencil(); // todo.. we might not want this for a skybox.
try pipelineBuilder.init_triangle_pipeline(gc.actual_extent);
pipelineBuilder.pdsci.?.depth_write_enable = vk.FALSE;
pipelineBuilder.pdsci.?.depth_test_enable = vk.FALSE;
pipelineBuilder.pdsci.?.depth_compare_op = .never;
const materialName = core.MakeName("Mat_skybox");
self.material = try self.allocator.create(graphics.Material);
self.material.* = graphics.Material{
.materialName = materialName,
.pipeline = (try pipelineBuilder.build(gc.renderPass)).?,
.layout = pipelineBuilder.pipelineLayout,
};
try gc.add_material(self.material);
}
pub fn setSkybox(textureName: []const u8) !void {
const name = core.MakeName(textureName);
gSkybox.cubeMapName = name;
}
pub fn destroy(self: *@This()) void {
self.allocator.destroy(self);
}
const vk_renderer_interface = @import("vk_renderer/vk_renderer_interface.zig");
const RendererInterface = vk_renderer_interface.RendererInterface;
const std = @import("std");
const graphics = @import("graphics.zig");
const core = @import("core");
const vk = @import("vulkan");
const skybox_vert = @import("skybox_vert");
const skybox_frag = @import("skybox_frag");
const vkinit = @import("vk_init.zig");

View File

@ -1,39 +0,0 @@
const std = @import("std");
const core = @import("core");
const vk_renderer = @import("vk_renderer.zig");
const vma = @import("vma");
const vk = @import("vulkan");
const vkinit = @import("vk_init.zig");
const NeonVkContext = vk_renderer.NeonVkContext;
const NeonVkBuffer = vk_renderer.NeonVkBuffer;
const NeonVkImage = vk_renderer.NeonVkImage;
pub const PixelPos = struct {
x: u32,
y: u32,
/// returns y/x of the pixel position
pub fn ratio(self: @This()) f32 {
return @as(f32, @floatFromInt(self.y)) / @as(f32, @floatFromInt(self.x));
}
};
// This is a simple display texture
pub const Texture = struct {
image: NeonVkImage,
imageView: vk.ImageView,
isCube: bool = false,
pub fn deinit(self: *@This(), ctx: *NeonVkContext) void {
ctx.vkd.destroyImageView(ctx.dev, self.imageView, null);
self.image.deinit(ctx.vkAllocator);
}
pub fn getDimensions(self: @This()) PixelPos {
return .{
.x = self.image.pixelWidth,
.y = self.image.pixelHeight,
};
}
};

View File

@ -1,382 +0,0 @@
// simple wrapper around vma with a very slow debug mode that
// shows every single vma event
//
// BECAUSE I CAN'T FIND WHERE I FAILED TO DESTROY SOME MEMORY.
const std = @import("std");
const vk = @import("vulkan");
const vma = @import("vma");
const core = @import("core");
const memory = core.MemoryTracker;
const vk_constants = @import("vk_constants.zig");
const DeviceDispatch = vk_constants.DeviceDispatch;
const BaseDispatch = vk_constants.BaseDispatch;
const InstanceDispatch = vk_constants.InstanceDispatch;
pub const Allocation = vma.Allocation;
pub const Allocator = vma.Allocator;
pub const AllocationCreateInfo = vma.AllocationCreateInfo;
pub const NeonVkBuffer = struct {
buffer: vk.Buffer,
allocation: vma.Allocation,
size: usize,
pub fn deinit(self: *@This(), vkAllocator: *NeonVkAllocator) void {
vkAllocator.destroyBuffer(self);
}
};
pub const NeonVkImage = struct {
image: vk.Image,
allocation: vma.Allocation,
pixelWidth: u32,
pixelHeight: u32,
pub fn deinit(self: *NeonVkImage, allocator: *NeonVkAllocator) void {
allocator.destroyImage(self);
}
/// returns the image ratio of the height over width
pub inline fn getImageRatioFloat(self: @This()) f32 {
return @as(f32, @floatFromInt(self.pixelHeight)) / @as(f32, @floatFromInt(self.pixelWidth));
}
};
pub const AllocationEvent = union(enum) {
allocate: struct {
alloc: usize,
tag: []const u8,
},
destroy: struct {
alloc: usize,
tag: []const u8,
},
pub fn print(self: @This()) void {
switch (self) {
.allocate => |allocate| {
core.graphics_log("allocate @{d} - {s}", .{ allocate.alloc, allocate.tag });
},
.destroy => |destroy| {
core.graphics_log("destroy @{d} - {s}", .{ destroy.alloc, destroy.tag });
},
}
}
};
pub const NeonVkAllocator = struct {
mutex: std.Thread.Mutex = .{},
vmaAllocator: vma.Allocator,
allocator: std.mem.Allocator,
eventsList: std.ArrayList(AllocationEvent),
liveAllocations: std.ArrayList(LiveAlloc),
livePipelines: std.AutoHashMap(u64, []u8),
vkb: vk_constants.BaseDispatch,
vki: vk_constants.InstanceDispatch,
vkd: vk_constants.DeviceDispatch,
const AllocatedObject = union {
image: NeonVkImage,
buffer: NeonVkBuffer,
};
const LiveAlloc = struct {
allocation: usize,
tag: []const u8,
object: AllocatedObject,
};
pub fn createStagingBuffer(
self: *@This(),
bufferSize: u32,
comptime tag: []const u8,
) !NeonVkBuffer {
const bci = vk.BufferCreateInfo{
.flags = .{},
.size = bufferSize,
.usage = .{ .transfer_src_bit = true },
.sharing_mode = .exclusive,
.queue_family_index_count = 0,
.p_queue_family_indices = undefined,
};
const vmaCreateInfo = vma.AllocationCreateInfo{
.flags = .{},
.usage = .cpuOnly,
};
return self.createBuffer(bci, vmaCreateInfo, tag);
}
pub fn createPipelineLayout(self: *@This(), dev: vk.Device, plci: vk.PipelineLayoutCreateInfo, tag: []const u8) !vk.PipelineLayout {
const pipelineLayout = try self.vkd.createPipelineLayout(dev, &plci, null);
try self.livePipelines.put(@intFromEnum(pipelineLayout), try core.dupeString(self.allocator, tag));
return pipelineLayout;
}
pub fn destroyPipelineLayout(self: *@This(), dev: vk.Device, layout: vk.PipelineLayout) void {
self.allocator.free(self.livePipelines.get(@intFromEnum(layout)).?);
_ = self.livePipelines.remove(@intFromEnum(layout));
self.vkd.destroyPipelineLayout(dev, layout, null);
}
pub fn createGpuBuffer(
self: *@This(),
bufferSize: u32,
options: struct {
index_buffer_bit: bool = false,
vertex_buffer_bit: bool = false,
uniform_texel_buffer_bit: bool = false,
storage_texel_buffer_bit: bool = false,
uniform_buffer_bit: bool = false,
storage_buffer_bit: bool = false,
indirect_buffer_bit: bool = false,
},
comptime tag: []const u8,
) !NeonVkBuffer {
const bci = vk.BufferCreateInfo{
.flags = .{},
.size = bufferSize,
.usage = .{
.transfer_dst_bit = true,
.index_buffer_bit = options.index_buffer_bit,
.vertex_buffer_bit = options.vertex_buffer_bit,
.uniform_buffer_bit = options.uniform_buffer_bit,
.storage_buffer_bit = options.storage_buffer_bit,
.indirect_buffer_bit = options.indirect_buffer_bit,
},
.sharing_mode = .exclusive,
.queue_family_index_count = 0,
.p_queue_family_indices = undefined,
};
const vmaCreateInfo = vma.AllocationCreateInfo{
.flags = .{},
.usage = .gpuOnly,
};
return self.createBuffer(bci, vmaCreateInfo, tag);
}
pub fn create(
vmaAllocatorCreateInfo: vma.AllocatorCreateInfo,
allocator: std.mem.Allocator,
vkb: vk_constants.BaseDispatch,
vki: vk_constants.InstanceDispatch,
vkd: vk_constants.DeviceDispatch,
) !*@This() {
const newAllocator = try allocator.create(@This());
newAllocator.* = @This(){
.vmaAllocator = try vma.Allocator.create(vmaAllocatorCreateInfo),
.allocator = allocator,
.eventsList = std.ArrayList(AllocationEvent).init(allocator),
.liveAllocations = std.ArrayList(LiveAlloc).init(allocator),
.livePipelines = std.AutoHashMap(u64, []u8).init(allocator),
.vkb = vkb,
.vki = vki,
.vkd = vkd,
};
return newAllocator;
}
fn pushAllocation(
self: *@This(),
allocation: vma.Allocation,
tag: []const u8,
object: AllocatedObject,
) !void {
try self.liveAllocations.append(.{
.allocation = @intFromEnum(allocation),
.tag = tag,
.object = object,
});
try self.eventsList.append(.{ .allocate = .{
.alloc = @intFromEnum(allocation),
.tag = tag,
} });
}
fn pushDestroy(self: *@This(), allocation: vma.Allocation) void {
// find the corresponding live allocation
var live: LiveAlloc = undefined;
var i: u32 = 0;
var found: bool = false;
while (i < self.liveAllocations.items.len) : (i += 1) {
if (self.liveAllocations.items[i].allocation == @intFromEnum(allocation)) {
found = true;
live = self.liveAllocations.items[i];
break;
}
}
if (found) {
_ = self.liveAllocations.swapRemove(i);
} else {
core.engine_log("We have a big issue here, a destroy was issued for allocation {any}\n But it is not alive", .{allocation});
self.printOutStandingAllocations();
unreachable;
}
self.eventsList.append(.{ .destroy = .{
.alloc = @intFromEnum(allocation),
.tag = live.tag,
} }) catch unreachable;
}
pub fn createIndirectCommandBuffer(
self: *@This(),
bufferSize: u32,
comptime tag: []const u8,
) !NeonVkBuffer {
return try self.createGpuBuffer(bufferSize, .{ .indirect_buffer_bit = true }, tag);
}
pub fn createSsboBuffer(self: *@This(), bufferSize: u32, comptime tag: []const u8) !NeonVkBuffer {
const bci = vk.BufferCreateInfo{
.size = bufferSize,
.usage = .{ .storage_buffer_bit = true },
.flags = .{},
.sharing_mode = .exclusive,
.queue_family_index_count = 0,
.p_queue_family_indices = undefined,
};
const aci = vma.AllocationCreateInfo{
.usage = .cpuToGpu,
};
return try self.createBuffer(bci, aci, tag);
}
pub fn createBuffer(
self: *@This(),
bci: vk.BufferCreateInfo,
aci: AllocationCreateInfo,
comptime tag: []const u8,
) !NeonVkBuffer {
self.mutex.lock();
defer self.mutex.unlock();
const results = try self.vmaAllocator.createBuffer(bci, aci);
const object: AllocatedObject = .{
.buffer = NeonVkBuffer{
.buffer = results.buffer,
.allocation = results.allocation,
.size = bci.size,
},
};
memory.MTAddUntrackedAllocation(bci.size);
try self.pushAllocation(results.allocation, tag, object);
return object.buffer;
}
pub fn destroyBuffer(self: *@This(), buffer: *NeonVkBuffer) void {
self.mutex.lock();
defer self.mutex.unlock();
memory.MTRemoveAllocation(buffer.size);
self.pushDestroy(buffer.allocation);
self.vmaAllocator.destroyBuffer(buffer.buffer, buffer.allocation);
}
pub fn destroyImage(self: *@This(), image: *NeonVkImage) void {
self.mutex.lock();
defer self.mutex.unlock();
self.pushDestroy(image.allocation);
self.vmaAllocator.destroyImage(image.image, image.allocation);
}
pub fn createImage(
self: *@This(),
ici: vk.ImageCreateInfo,
aci: AllocationCreateInfo,
comptime tag: []const u8,
) !NeonVkImage {
self.mutex.lock();
defer self.mutex.unlock();
const result = try self.vmaAllocator.createImage(ici, aci);
const object: AllocatedObject = .{ .image = .{
.image = result.image,
.allocation = result.allocation,
.pixelWidth = ici.extent.width,
.pixelHeight = ici.extent.height,
} };
try self.pushAllocation(result.allocation, tag, object);
return object.image;
}
pub fn mapBuffer(self: *@This(), comptime T: type, buffer: NeonVkBuffer) ![]T {
var slice: []T = undefined;
slice.ptr = try self.mapMemory(buffer, T);
slice.len = buffer.size / @sizeOf(T);
return slice;
}
pub fn mapMemorySlice(self: *@This(), comptime T: type, buffer: NeonVkBuffer, size: usize) ![]T {
var slice: []T = undefined;
slice.ptr = try self.mapMemory(buffer, T);
slice.len = size;
return slice;
}
pub fn mapMemory(self: *@This(), buffer: NeonVkBuffer, comptime T: type) ![*]T {
return try self.vmaAllocator.mapMemory(buffer.allocation, T);
}
pub fn unmapMemory(self: *@This(), buffer: NeonVkBuffer) void {
self.vmaAllocator.unmapMemory(buffer.allocation);
}
pub fn printEventsLog(self: @This()) void {
for (self.eventsList.items) |item| {
item.print();
}
}
pub fn areAllocationsOutstanding(self: *@This()) bool {
return self.liveAllocations.items.len > 0;
}
pub fn printOutStandingAllocations(self: *@This()) void {
core.graphics_log(" == There are {d} allocations outstanding", .{self.liveAllocations.items.len});
for (self.liveAllocations.items) |alloc| {
core.graphics_log("live allocation@{d} tag:\'{s}\' {any}", .{ alloc.allocation, alloc.tag, alloc.object });
}
core.graphics_logs("--- Event log below --- ");
self.printEventsLog();
core.graphics_logs("end of report.");
core.forceFlush();
}
pub fn destroy(self: *@This()) void {
self.eventsList.deinit();
self.liveAllocations.deinit();
{
var iter = self.livePipelines.iterator();
while (iter.next()) |i| {
self.allocator.free(i.value_ptr.*);
}
}
self.livePipelines.deinit();
self.vmaAllocator.destroy();
self.allocator.destroy(self);
}
};

View File

@ -1,16 +0,0 @@
// global api
//
// i hate lugging these variables around.
// device and cmd buffers are fine,
// but the dispatch variables are going to be kept here and easily accessible.
pub var _vkb: constants.BaseDispatch = undefined;
pub var _vki: constants.InstanceDispatch = undefined;
pub var _vkd: constants.DeviceDispatch = undefined;
pub const vkb = &_vkb;
pub const vki = &_vki;
pub const vkd = &_vkd;
const vk = @import("vulkan");
const constants = @import("vk_constants.zig");

View File

@ -1,373 +0,0 @@
const std = @import("std");
const vk = @import("vulkan");
const graphics = @import("graphics.zig");
const core = @import("core");
const assets = @import("assets");
const vk_utils = @import("vk_utils.zig");
const vkinit = @import("vk_init.zig");
const vk_cubemap = @import("vk_renderer/vk_cubemap.zig");
const tracy = core.tracy;
const materials = @import("materials.zig");
const vk_renderer = @import("vk_renderer.zig");
const mesh = @import("mesh.zig");
const texture = @import("texture.zig");
const NeonVkContext = vk_renderer.NeonVkContext;
const Material = materials.Material;
const Mesh = mesh.Mesh;
const Texture = texture.Texture;
pub const TextureLoader = struct {
pub var LoaderInterfaceVTable: assets.AssetLoaderInterface = assets.AssetLoaderInterface.from("Texture", @This());
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
const StagedTextureDescription = struct {
name: core.Name,
stagingResults: vk_utils.LoadAndStageImage,
textureListResults: ?[]vk_utils.LoadAndStageImage = null,
assetRef: assets.AssetRef,
properties: assets.AssetPropertiesBag,
pub fn deinit(self: *@This(), gc: *NeonVkContext) void {
self.stagingResults.deinit(gc.vkAllocator);
if (self.textureListResults) |results| {
for (results) |*result| {
result.deinit(gc.vkAllocator);
}
}
}
};
const RTAssetsReady = struct {
name: core.Name,
texture: *Texture,
textureSet: vk.DescriptorSet,
textureId: u32,
};
gc: *NeonVkContext,
assetsReady: core.RingQueue(StagedTextureDescription),
rtAssetsReady: core.RingQueue(RTAssetsReady),
discarding: std.atomic.Value(bool) = std.atomic.Value(bool).init(false),
pub fn loadAsset(self: *@This(), assetRef: assets.AssetRef, props: ?assets.AssetPropertiesBag) assets.AssetLoaderError!void {
if (self.discarding.load(.seq_cst)) {
return;
}
var z = tracy.ZoneN(@src(), "TextureLoader loadAsset");
const Lambda = struct {
loader: *TextureLoader,
assetRef: assets.AssetRef,
gc: *NeonVkContext,
properties: assets.AssetPropertiesBag,
pub fn eFunc(ctx: @This()) !void {
var z1 = tracy.ZoneN(@src(), "Loading file from TextureLoader");
const gc = ctx.gc;
defer {
_ = ctx.gc.outstandingJobsCount.fetchSub(1, .seq_cst);
}
var loadAndStageResults: vk_utils.LoadAndStageImage = undefined;
if (!ctx.properties.textureCube) {
loadAndStageResults = try vk_utils.load_and_stage_image_from_file(gc, ctx.properties.path);
errdefer loadAndStageResults.deinit(gc.vkAllocator);
} else {
loadAndStageResults = try vk_cubemap.stageCubeTexture(ctx.properties.textureList.?);
errdefer loadAndStageResults.deinit(gc.vkAllocator);
}
var assetRefName = ctx.assetRef.name;
tracy.Message(assetRefName.utf8());
tracy.Message(ctx.properties.path);
core.engine_log("loaded: {s} from: {s}", .{ assetRefName.utf8(), ctx.properties.path });
var loadedDescription = StagedTextureDescription{
.name = ctx.assetRef.name,
.stagingResults = loadAndStageResults,
.assetRef = ctx.assetRef,
.properties = ctx.properties,
};
if (ctx.properties.textureList) |textureList| {
const tlResults = try ctx.gc.allocator.alloc(vk_utils.LoadAndStageImage, textureList.len);
errdefer ctx.gc.allocator.free(tlResults);
for (textureList, 0..) |tPath, i| {
const rv = vk_utils.load_and_stage_image_from_file(gc, tPath) catch {
core.engine_log("unable to load file {s}", .{tPath});
return error.FailedToLoad;
};
errdefer rv.deinit();
tlResults[i] = rv;
}
loadedDescription.textureListResults = tlResults;
}
z1.End();
ctx.loader.assetsReady.pushLocked(loadedDescription) catch unreachable;
}
pub fn func(ctx: @This(), _: *core.JobContext) void {
ctx.eFunc() catch unreachable;
}
};
_ = self.gc.outstandingJobsCount.fetchAdd(1, .seq_cst);
core.dispatchJob(Lambda{
.loader = self,
.gc = self.gc,
.assetRef = assetRef,
.properties = props.?,
}) catch return error.UnableToLoad;
z.End();
}
pub fn processRenderThreadEvents(ptr: *anyopaque) void {
const self: *@This() = @ptrCast(@alignCast(ptr));
self.processEventInner() catch {};
}
pub fn createImageFromStagingResult(self: *@This(), name: core.Name, stagingResults: *vk_utils.LoadAndStageImage, properties: assets.AssetPropertiesBag) core.EngineDataEventError!void {
const gc = self.gc;
var stagingBuffer = stagingResults.stagingBuffer;
const image = stagingResults.image;
if (stagingResults.cubeOffsets != null) {
vk_cubemap.submitTextureCube(&gc.uploader, stagingResults) catch return error.UnknownStatePanic;
stagingBuffer.deinit(gc.vkAllocator);
var ivc = vkinit.imageViewCreateInfo(
.r8g8b8a8_srgb,
image.image,
.{ .color_bit = true },
stagingResults.mipLevel,
);
ivc.view_type = .cube;
ivc.subresource_range.layer_count = 6;
const imageView = gc.vkd.createImageView(gc.dev, &ivc, null) catch return error.UnknownStatePanic;
const newTexture = gc.allocator.create(Texture) catch return error.UnknownStatePanic;
newTexture.* = Texture{
.image = image,
.imageView = imageView,
};
const rv = vk_utils.createDescriptorSetForImage(
gc.dev,
gc.descriptorPool,
gc.singleTextureSetLayout,
imageView,
gc.cubeSampler,
false,
) catch return error.UnknownStatePanic;
self.rtAssetsReady.pushLocked(.{
.name = name,
.texture = newTexture,
.textureSet = rv.textureSet,
.textureId = rv.textureId,
}) catch return error.UnknownStatePanic;
} else {
vk_utils.submit_copy_from_staging(gc, stagingBuffer, image, stagingResults.mipLevel) catch return error.UnknownStatePanic;
stagingBuffer.deinit(gc.vkAllocator);
var imageViewCreate = vkinit.imageViewCreateInfo(
.r8g8b8a8_srgb,
image.image,
.{ .color_bit = true },
stagingResults.mipLevel,
);
const imageView = gc.vkd.createImageView(gc.dev, &imageViewCreate, null) catch return error.UnknownStatePanic;
const newTexture = gc.allocator.create(Texture) catch return error.UnknownStatePanic;
newTexture.* = Texture{
.image = image,
.imageView = imageView,
};
const sampler = if (properties.textureUseBlockySampler) gc.blockySampler else gc.linearSampler;
const rv = vk_utils.createDescriptorSetForImage(
gc.dev,
gc.descriptorPool,
gc.singleTextureSetLayout,
imageView,
sampler,
true,
) catch return error.UnknownStatePanic;
self.rtAssetsReady.pushLocked(.{
.name = name,
.texture = newTexture,
.textureSet = rv.textureSet,
.textureId = rv.textureId,
}) catch return error.UnknownStatePanic;
}
}
fn processEventInner(self: *@This()) core.EngineDataEventError!void {
if (self.assetsReady.count() > 0) {
self.assetsReady.lock();
defer self.assetsReady.unlock();
while (self.assetsReady.popFromUnlocked()) |ar| {
var assetReady = ar;
var z1 = tracy.ZoneN(@src(), "Uploading asset loaded by TextureLoader");
tracy.Message("TextureLoader");
tracy.Message(assetReady.assetRef.name.utf8());
tracy.Message(assetReady.properties.path);
core.engine_log("async texture load complete registry: {s}", .{assetReady.name.utf8()});
try self.createImageFromStagingResult(assetReady.name, &assetReady.stagingResults, assetReady.properties);
if (assetReady.textureListResults) |results| {
var buf: [256]u8 = undefined;
for (results, 0..) |res, i| {
var r = res;
var arName = assetReady.name;
const newName = std.fmt.bufPrint(&buf, "{s}[{d}]", .{ arName.utf8(), i }) catch return core.EngineDataEventError.OutOfMemory;
try self.createImageFromStagingResult(core.MakeName(newName), &r, assetReady.properties);
}
self.gc.allocator.free(results);
}
z1.End();
}
}
}
// processing events, some should really be processing events rather than
pub fn processEvents(self: *@This(), frameNumber: u64) core.EngineDataEventError!void {
_ = frameNumber;
if (self.rtAssetsReady.count() > 0) {
self.rtAssetsReady.lock();
defer self.rtAssetsReady.unlock();
while (self.rtAssetsReady.popFromUnlocked()) |a| {
self.gc.install_texture_into_registry(a.name, a.texture, a.textureSet, a.textureId) catch return error.UnknownStatePanic;
}
}
}
pub fn discardAll(self: *@This()) void {
self.discarding.store(true, .seq_cst);
core.graphics_log("discarding {d} outstanding jobs", .{self.assetsReady.count()});
self.assetsReady.lock();
defer self.assetsReady.unlock();
while (self.assetsReady.popFromUnlocked()) |assetReady| {
var copy = assetReady;
StagedTextureDescription.deinit(&copy, self.gc);
}
}
pub fn init(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.gc = vk_renderer.gContext,
//todo: the EngineObjectVTable init function should have a handleable error
.assetsReady = core.RingQueue(StagedTextureDescription).init(allocator, 1024) catch unreachable,
.rtAssetsReady = core.RingQueue(RTAssetsReady).init(allocator, 1024) catch unreachable,
};
try self.gc.renderthread.installListener(self, processRenderThreadEvents);
return self;
}
pub fn destroy(self: *@This(), allocator: std.mem.Allocator) void {
self.assetsReady.deinit();
self.rtAssetsReady.deinit();
allocator.destroy(self);
}
};
pub const MeshLoader = struct {
pub var LoaderInterfaceVTable = assets.AssetLoaderInterface.from("Mesh", @This());
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
gc: *NeonVkContext,
pub fn init(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.gc = vk_renderer.gContext,
};
return self;
}
pub fn loadAsset(self: *@This(), assetRef: assets.AssetRef, propertiesBag: ?assets.AssetPropertiesBag) assets.AssetLoaderError!void {
_ = self;
const sourceType = getSourceType(propertiesBag);
core.engine_log("loading mesh asset {s} [{s}]", .{ propertiesBag.?.path, if (sourceType) |s| @tagName(s) else "default" });
graphics.loadIndexedMeshForPooling(assetRef.name, .{
.path = propertiesBag.?.path,
.sourceType = getSourceType(propertiesBag),
.skeletonName = if (propertiesBag.?.skeletonName) |skName| core.MakeName(skName) else null,
}) catch return error.UnableToLoad;
}
fn getSourceType(propertiesBag: ?assets.AssetPropertiesBag) ?graphics.MeshSourceType {
if (propertiesBag) |bag| {
if (bag.meshType) |meshType| {
if (std.mem.eql(u8, meshType, "obj")) {
return graphics.MeshSourceType.obj;
}
if (std.mem.eql(u8, meshType, "gltf")) {
return graphics.MeshSourceType.gltf;
}
}
// try to deduce it by file name, if nothing is set.
const ext = core.getFileExtension(bag.path);
if (std.mem.eql(u8, ext, ".obj")) {
return graphics.MeshSourceType.obj;
}
if (std.mem.eql(u8, ext, ".gltf")) {
return graphics.MeshSourceType.gltf;
}
if (std.mem.eql(u8, ext, ".glb")) {
return graphics.MeshSourceType.gltf;
}
}
return null;
}
pub fn discardAll(self: *@This()) void {
// totally synchronous, nothing to do for a discard
_ = self;
}
pub fn destroy(self: *@This(), allocator: std.mem.Allocator) void {
allocator.destroy(self);
}
};
pub var gTextureLoader: *TextureLoader = undefined;
pub var gMeshLoader: *MeshLoader = undefined;
pub fn init_loaders(allocator: std.mem.Allocator) !void {
gTextureLoader = try core.createObject(TextureLoader, .{
.responds_to_events = true,
});
gMeshLoader = try allocator.create(MeshLoader);
gMeshLoader.* = .{ .gc = vk_renderer.gContext };
try assets.gAssetSys.registerLoader(gTextureLoader);
try assets.gAssetSys.registerLoader(gMeshLoader);
}
// submit an abort message to TextureLoader and MeshLoader
pub fn discardAll() void {
gTextureLoader.discardAll();
gMeshLoader.discardAll();
}

View File

@ -1,114 +0,0 @@
const std = @import("std");
const vk = @import("vulkan");
const core = @import("core");
pub const NumFrames = NUM_FRAMES;
pub const FrameTimeout = 10_000_000_000; // 10 full second frame timeout
pub const MAX_OBJECTS = 100_000; // 100k objects ought to be enough for anyone
pub const NUM_FRAMES: usize = 2;
pub const DEVICE_LAYERS = [_]core.CStr{VK_KHRONOS_VALIDATION_LAYER_STRING};
pub const MAX_SKIN_SLOTS = 100_000;
pub const required_device_layers = [_]core.CStr{"VK_LAYER_KHRONOS_validation"};
pub const VK_KHRONOS_VALIDATION_LAYER_STRING: core.CStr = "VK_LAYER_KHRONOS_validation";
pub const BaseDispatch = vk.BaseWrapper(.{
.createInstance = true,
.getInstanceProcAddr = true,
.enumerateInstanceVersion = true,
.enumerateInstanceLayerProperties = true,
.enumerateInstanceExtensionProperties = true,
});
pub const InstanceDispatch = vk.InstanceWrapper(.{
.getPhysicalDeviceFeatures = true,
.destroyInstance = true,
.createDevice = true,
.destroySurfaceKHR = true,
.enumeratePhysicalDevices = true,
.getPhysicalDeviceProperties = true,
.enumerateDeviceExtensionProperties = true,
.getPhysicalDeviceSurfaceFormatsKHR = true,
.getPhysicalDeviceSurfacePresentModesKHR = true,
.getPhysicalDeviceSurfaceCapabilitiesKHR = true,
.getPhysicalDeviceQueueFamilyProperties = true,
.getPhysicalDeviceSurfaceSupportKHR = true,
.getPhysicalDeviceMemoryProperties = true,
.getPhysicalDeviceFormatProperties = true,
.getDeviceProcAddr = true,
});
pub const DeviceDispatch = vk.DeviceWrapper(.{
.resetCommandBuffer = true,
.destroyDevice = true,
.getDeviceQueue = true,
.createSemaphore = true,
.createFence = true,
.createImageView = true,
.createImage = true,
.destroyImage = true,
.destroyImageView = true,
.destroySemaphore = true,
.destroyFence = true,
.getSwapchainImagesKHR = true,
.createSwapchainKHR = true,
.destroySwapchainKHR = true,
.acquireNextImageKHR = true,
.deviceWaitIdle = true,
.waitForFences = true,
.resetFences = true,
.queueSubmit = true,
.queuePresentKHR = true,
.createCommandPool = true,
.destroyCommandPool = true,
.allocateCommandBuffers = true,
.cmdBlitImage = true,
.freeCommandBuffers = true,
.queueWaitIdle = true,
.createShaderModule = true,
.destroyShaderModule = true,
.createPipelineLayout = true,
.destroyPipelineLayout = true,
.createDescriptorSetLayout = true,
.destroyDescriptorSetLayout = true,
.createDescriptorPool = true,
.allocateDescriptorSets = true,
.freeDescriptorSets = true,
.updateDescriptorSets = true,
.destroyDescriptorPool = true,
.createRenderPass = true,
.destroyRenderPass = true,
.createGraphicsPipelines = true,
.destroyPipeline = true,
.createFramebuffer = true,
.destroyFramebuffer = true,
.beginCommandBuffer = true,
.endCommandBuffer = true,
.allocateMemory = true,
.freeMemory = true,
.createBuffer = true,
.destroyBuffer = true,
.getBufferMemoryRequirements = true,
.mapMemory = true,
.unmapMemory = true,
.bindBufferMemory = true,
.cmdBeginRenderPass = true,
.cmdEndRenderPass = true,
.cmdBindPipeline = true,
.cmdBindIndexBuffer = true,
.cmdDrawIndexed = true,
.cmdDraw = true,
.cmdSetViewport = true,
.cmdSetScissor = true,
.cmdBindVertexBuffers = true,
.cmdCopyBuffer = true,
.cmdPushConstants = true,
.cmdPipelineBarrier = true,
.cmdBindDescriptorSets = true,
.cmdCopyBufferToImage = true,
.createSampler = true,
.destroySampler = true,
.cmdDrawIndexedIndirect = true,
});

View File

@ -1,25 +0,0 @@
// higher level descriptor and SSBO wrangling libraries.
const std = @import("std");
const core = @import("core");
const vk_renderer = @import("vk_renderer.zig");
const vma = @import("vma");
const vk = @import("vulkan");
const obj_loader = @import("objLoader");
const vkinit = @import("vk_init.zig");
const vk_constants = @import("vk_constants.zig");
const tracy = core.tracy;
const spng = core.spng;
const ObjMesh = obj_loader.ObjMesh;
const ArrayList = std.ArrayList;
const Vectorf = core.Vectorf;
const NeonVkContext = vk_renderer.NeonVkContext;
const NeonVkBuffer = vk_renderer.NeonVkBuffer;
const NeonVkImage = vk_renderer.NeonVkImage;
const NumFrames = vk_constants.NUM_FRAMES;
pub const DescriptorSetLayoutInfo = struct {
bindingCount: u32,
};

View File

@ -1,435 +0,0 @@
const std = @import("std");
const core = @import("core");
const vk_renderer = @import("vk_renderer.zig");
const vma = @import("vma");
const vk = @import("vulkan");
const obj_loader = @import("objLoader");
const constants = @import("vk_constants.zig");
const vk_utils = @import("vk_utils.zig");
const NeonVkUploader = vk_utils.NeonVkUploader;
const NeonVkBuffer = vk_renderer.NeonVkBuffer;
const ObjMesh = obj_loader.ObjMesh;
const ArrayList = std.ArrayList;
const Vectorf = core.Vectorf;
const Vector2f = core.Vector2f;
const LinearColor = core.colors.Color;
const NeonVkContext = vk_renderer.NeonVkContext;
const mesh = @import("mesh.zig");
const MeshVertex = mesh.MeshVertex;
const debug_struct = core.debug_struct;
pub const DynamicMeshManager = struct {
gc: *NeonVkContext,
allocator: std.mem.Allocator,
dynMeshes: std.ArrayListUnmanaged(*DynamicMesh) = .{},
uploader: NeonVkUploader,
first: bool = true,
pub fn init(gc: *NeonVkContext) !*@This() {
const self = try gc.allocator.create(@This());
self.* = @This(){
.gc = gc,
.allocator = gc.allocator,
.uploader = try NeonVkUploader.init(gc, "dynamic mesh manager uploader"),
};
// core.graphics_log("creating the mesh manager", .{});
return self;
}
pub fn deinit(self: *@This()) void {
self.uploader.deinit();
self.dynMeshes.deinit(self.allocator);
self.allocator.destroy(self);
}
pub fn addDynamicMesh(self: *@This(), dynamicMesh: *DynamicMesh) !void {
try self.dynMeshes.append(self.allocator, dynamicMesh);
}
pub fn updateMeshes(self: *@This(), cmd: vk.CommandBuffer) !void {
for (self.dynMeshes.items) |dynMesh| {
try dynMesh.maybeUpdateVertices(cmd);
}
}
pub fn finishUpload(self: *@This()) !void {
if (self.uploader.isActive) {
var t3 = core.tracy.ZoneN(@src(), "finishing dynamic mesh upload context");
defer t3.End();
try self.uploader.waitForFences();
for (self.dynMeshes.items) |dynMesh| {
if (dynMesh.isDirty) {
dynMesh.bumpSwapId();
}
}
}
}
};
pub const DynamicMesh = struct {
pub const GeometryMode = enum { quads, triangles };
allocator: std.mem.Allocator,
gc: *NeonVkContext,
vertices: []MeshVertex = undefined,
geometryMode: GeometryMode = .quads, // geometry elaboration mode
indicesMaxCount: u32 = 0,
indexBuffers: [2]NeonVkBuffer = undefined,
indexBufferLen: [2]u32 = .{ 0, 0 },
vertexBuffers: [2]NeonVkBuffer = undefined,
vertexBufferLen: [2]u32 = .{ 0, 0 },
swapId: usize = 0, // the index of the previously uploaded vertex buffer
vertexCount: u32 = 0, //
stagingVertexBuffer: NeonVkBuffer = undefined,
stagingIndexBuffer: NeonVkBuffer = undefined,
isDirty: bool = true,
maxVertexCount: u32,
pub fn init(gc: *NeonVkContext, allocator: std.mem.Allocator, opts: struct {
maxVertexCount: u32 = 4096,
maxIndexCount: u32 = 4096 * 6 / 4,
mode: GeometryMode = .quads,
}) !*@This() {
var self = try allocator.create(@This());
self.* = .{
.maxVertexCount = opts.maxVertexCount,
.allocator = allocator,
.vertices = try allocator.alloc(MeshVertex, opts.maxVertexCount),
.gc = gc,
.geometryMode = opts.mode,
};
try gc.dynamicMeshManager.addDynamicMesh(self);
self.allocator = allocator;
self.gc = gc;
{
self.stagingIndexBuffer = try gc.vkAllocator.createStagingBuffer(opts.maxIndexCount * @sizeOf(u32), "DynamicMesh.init - index staging");
self.stagingVertexBuffer = try gc.vkAllocator.createStagingBuffer(opts.maxVertexCount * @sizeOf(MeshVertex), "DynamicMesh.init - vertex staging");
inline for (0..2) |i| {
self.indexBuffers[i] = try gc.vkAllocator.createGpuBuffer(opts.maxIndexCount * @sizeOf(u32), .{
.index_buffer_bit = true,
}, "DynamicMesh.init - gpu indexBuffer" ++ std.fmt.comptimePrint("[{d}]", .{i}));
self.vertexBuffers[i] = try gc.vkAllocator.createGpuBuffer(opts.maxVertexCount * @sizeOf(MeshVertex), .{
.vertex_buffer_bit = true,
}, "DynamicMesh.init - gpu vertexBuffer" ++ std.fmt.comptimePrint("[{d}]", .{i}));
}
}
return self;
}
pub fn getIndexBuffer(self: *@This()) NeonVkBuffer {
return self.indexBuffers[self.swapId];
}
pub fn getVertexBuffer(self: *@This()) NeonVkBuffer {
return self.vertexBuffers[self.swapId];
}
pub fn getIndexBufferLen(self: *@This()) u32 {
return self.indexBufferLen[self.swapId];
}
pub fn getVertexBufferLen(self: *@This()) u32 {
return self.vertexBufferLen[self.swapId];
}
pub fn maybeUpdateVertices(self: *@This(), cmd: vk.CommandBuffer) !void {
if (!self.isDirty) {
return;
}
var t1 = core.tracy.ZoneN(@src(), "Dynamic Mesh upload with barriers");
defer t1.End();
self.isDirty = false;
try self.stageDirtyVertices();
if (self.indexBufferLen[self.swapId] == 0 or self.vertexCount == 0) {
return;
}
var vkd = self.gc.vkd;
var copy = vk.BufferCopy{
.dst_offset = 0,
.src_offset = 0,
.size = self.indexBufferLen[self.swapId] * @as(u32, @intCast(@sizeOf(u32))),
};
// submit index Buffer
self.gc.vkd.cmdCopyBuffer(
cmd,
self.stagingIndexBuffer.buffer,
self.indexBuffers[self.swapId].buffer,
1,
@as([*]const vk.BufferCopy, @ptrCast(&copy)),
);
var indexMemoryBarrier = vk.BufferMemoryBarrier{
.buffer = self.indexBuffers[self.swapId].buffer,
.src_access_mask = .{ .transfer_read_bit = true },
.dst_access_mask = .{ .index_read_bit = true },
.src_queue_family_index = 0,
.dst_queue_family_index = 0,
.offset = 0,
.size = copy.size,
};
// Insert Barrier for indexBuffer
vkd.cmdPipelineBarrier(
cmd,
.{ .transfer_bit = true },
.{ .vertex_input_bit = true },
.{},
0,
undefined,
1,
@ptrCast(&indexMemoryBarrier),
0,
undefined,
);
copy.size = self.vertexCount * @as(u32, @intCast(@sizeOf(MeshVertex)));
// submit vertex Buffer
self.gc.vkd.cmdCopyBuffer(
cmd,
self.stagingVertexBuffer.buffer,
self.vertexBuffers[self.swapId].buffer,
1,
@as([*]const vk.BufferCopy, @ptrCast(&copy)),
);
// Insert Barrier for vertexBuffer
var vertexMemoryBarrier = vk.BufferMemoryBarrier{
.buffer = self.vertexBuffers[self.swapId].buffer,
.src_access_mask = .{
.transfer_read_bit = true,
},
.dst_access_mask = .{
// .transfer_write_bit = true,
.vertex_attribute_read_bit = true,
},
.src_queue_family_index = 0,
.dst_queue_family_index = 0,
.offset = 0,
.size = copy.size,
};
vkd.cmdPipelineBarrier(
cmd,
.{ .transfer_bit = true },
.{ .vertex_input_bit = true },
.{},
0,
undefined,
1,
@ptrCast(&vertexMemoryBarrier),
0,
undefined,
);
}
pub fn stageDirtyVertices(self: *@This()) !void {
const newSwapId = (self.swapId + 1) % 2;
// map buffers
var slice = try self.gc.vkAllocator.mapMemorySlice(MeshVertex, self.stagingVertexBuffer, self.vertices.len);
var indexSlice = try self.gc.vkAllocator.mapMemorySlice(u32, self.stagingIndexBuffer, self.vertices.len * 6 / 4);
defer self.gc.vkAllocator.unmapMemory(self.stagingVertexBuffer);
defer self.gc.vkAllocator.unmapMemory(self.stagingIndexBuffer);
// copy over vertices to mapped buffer
for (0..self.vertexCount) |i| {
slice[i] = self.vertices[i];
}
self.vertexBufferLen[newSwapId] = self.vertexCount;
// interpret vertices as quads.
if (self.geometryMode == .quads) {
var index: u32 = 0;
var vertex: u32 = 0;
while (vertex < self.vertexCount) {
indexSlice[index + 0] = vertex + 0;
indexSlice[index + 1] = vertex + 1;
indexSlice[index + 2] = vertex + 2;
indexSlice[index + 3] = vertex + 2;
indexSlice[index + 4] = vertex + 3;
indexSlice[index + 5] = vertex + 0;
vertex += 4;
index += 6;
}
self.indexBufferLen[newSwapId] = index;
}
self.swapId = newSwapId;
}
// a stream-like interface for creating vertices
// pub fn uploadVertices(self: *@This(), uploader: *NeonVkUploader) !void {
// if (!self.isDirty) {
// return;
// }
// self.dirty = false;
// const newSwapId = (self.swapId + 1) % 2;
// // map buffers
// var slice = try self.gc.vkAllocator.mapMemorySlice(MeshVertex, self.stagingVertexBuffer, self.vertices.len);
// var indexSlice = try self.gc.vkAllocator.mapMemorySlice(u32, self.stagingIndexBuffer, self.vertices.len * 6 / 4);
// defer self.gc.vkAllocator.unmapMemory(self.stagingVertexBuffer);
// defer self.gc.vkAllocator.unmapMemory(self.stagingIndexBuffer);
// // copy over vertices to mapped buffer
// // so... this right here would need to lock.. actually this would be a try-lock
// // if we fail to lock it... that's ok. we can just try again at the end of the frame.
// // what happens if we always fail to lock it?
// for (0..self.vertexCount) |i| {
// slice[i] = self.vertices[i];
// }
// self.vertexBufferLen[newSwapId] = self.vertexCount;
// // interpret vertices as quads.
// if (self.geometryMode == .quads) {
// var index: u32 = 0;
// var vertex: u32 = 0;
// while (vertex < self.vertexCount) {
// indexSlice[index + 0] = vertex + 0;
// indexSlice[index + 1] = vertex + 1;
// indexSlice[index + 2] = vertex + 2;
// indexSlice[index + 3] = vertex + 2;
// indexSlice[index + 4] = vertex + 3;
// indexSlice[index + 5] = vertex + 0;
// vertex += 4;
// index += 6;
// }
// self.indexBufferLen[newSwapId] = index;
// }
// // upload index and vertex buffers
// try uploader.addBufferUpload(
// self.stagingIndexBuffer,
// self.indexBuffers[newSwapId],
// self.indexBufferLen[newSwapId] * @as(u32, @intCast(@sizeOf(u32))),
// );
// try uploader.addBufferUpload(
// self.stagingVertexBuffer,
// self.vertexBuffers[newSwapId],
// self.vertexCount * @as(u32, @intCast(@sizeOf(MeshVertex))),
// );
// }
pub fn bumpSwapId(self: *@This()) void {
self.swapId = (self.swapId + 1) % 2;
self.isDirty = false;
}
pub fn clearVertices(self: *@This()) void {
self.vertexCount = 0;
self.isDirty = true;
}
pub fn addVertexList(self: *@This(), list: []const MeshVertex) void {
if (list.len > 0) {
self.isDirty = true;
}
for (list) |v| {
self.vertices[self.vertexCount] = v;
self.vertexCount += 1;
}
}
// adds a quad only in the X and y Space,
pub fn addQuad2D(
self: *@This(),
_topLeft: core.Vectorf, // only x and y is considered
_size: core.Vectorf, // only x and y is considered
topLeftUV: core.Vector2f,
uvSize: core.Vector2f,
color: LinearColor,
) void {
var topLeft = _topLeft;
var size = _size;
topLeft.z = 0;
size.z = 0;
const normal = Vectorf{ .x = 0, .y = 0, .z = -1 };
var vertices: [4]MeshVertex = undefined;
vertices[0] = .{
.position = topLeft,
.normal = normal,
.uv = topLeftUV,
.color = color,
};
vertices[1] = .{
.position = topLeft.add(.{ .x = size.x }),
.normal = normal,
.uv = topLeftUV.add(core.Vector2f{ .x = uvSize.x }),
.color = color,
};
vertices[2] = .{
.position = topLeft.add(size),
.normal = normal,
.uv = topLeftUV.add(uvSize),
.color = color,
};
vertices[3] = .{
.position = topLeft.add(.{ .y = size.y }),
.normal = normal,
.uv = topLeftUV.add(core.Vector2f{ .y = uvSize.y }),
.color = color,
};
self.addVertexList(&vertices);
}
pub fn deinit(self: *@This()) void {
self.allocator.free(self.vertices);
const vkAllocator = self.gc.vkAllocator;
self.stagingVertexBuffer.deinit(vkAllocator);
self.stagingIndexBuffer.deinit(vkAllocator);
for (0..self.indexBuffers.len) |i| {
self.indexBuffers[i].deinit(vkAllocator);
self.vertexBuffers[i].deinit(vkAllocator);
}
self.allocator.destroy(self);
// should remove ourselves from the manager
}
};

View File

@ -1,176 +0,0 @@
const vk = @import("vulkan");
const core = @import("core");
pub fn descriptorSetLayoutBinding(
descriptorType: vk.DescriptorType,
stageFlags: vk.ShaderStageFlags,
binding: u32,
) vk.DescriptorSetLayoutBinding {
return vk.DescriptorSetLayoutBinding{
.binding = binding,
.descriptor_count = 1,
.descriptor_type = descriptorType,
.stage_flags = stageFlags,
.p_immutable_samplers = null,
};
}
pub fn writeDescriptorSet(
descriptorType: vk.DescriptorType,
dst_set: vk.DescriptorSet,
bufferInfo: *vk.DescriptorBufferInfo,
binding: u32,
) vk.WriteDescriptorSet {
const setWrite = vk.WriteDescriptorSet{
.dst_binding = binding,
.dst_set = dst_set,
.descriptor_count = 1,
.descriptor_type = descriptorType,
.p_buffer_info = @ptrCast(bufferInfo),
.dst_array_element = 0,
.p_image_info = undefined,
.p_texel_buffer_view = undefined,
};
return setWrite;
}
pub fn commandPoolCreateInfo(
queueFamilyIndex: u32,
flags: vk.CommandPoolCreateFlags,
) vk.CommandPoolCreateInfo {
const self = vk.CommandPoolCreateInfo{
.queue_family_index = queueFamilyIndex,
.flags = flags,
};
return self;
}
pub fn submitInfo(cmd: *vk.CommandBuffer) vk.SubmitInfo {
const info = vk.SubmitInfo{
.wait_semaphore_count = 0,
.signal_semaphore_count = 0,
.command_buffer_count = 1,
.p_command_buffers = @as([*]const vk.CommandBuffer, @ptrCast(cmd)),
.p_wait_semaphores = undefined,
.p_wait_dst_stage_mask = undefined,
.p_signal_semaphores = undefined,
};
return info;
}
pub fn commandBufferBeginInfo(flags: vk.CommandBufferUsageFlags) vk.CommandBufferBeginInfo {
const cbi = vk.CommandBufferBeginInfo{
.p_inheritance_info = null,
.flags = flags,
};
return cbi;
}
pub fn imageCreateInfo(
format: vk.Format,
usageFlags: vk.ImageUsageFlags,
extent: vk.Extent3D,
mipLevel: u32, // should default to 1
) vk.ImageCreateInfo {
const img_create = vk.ImageCreateInfo{
.flags = .{},
.sharing_mode = .exclusive,
.queue_family_index_count = 0,
.p_queue_family_indices = undefined,
.initial_layout = .undefined,
.image_type = .@"2d",
.format = format,
.extent = extent,
.mip_levels = mipLevel,
.array_layers = 1,
.samples = .{
.@"1_bit" = true,
},
.tiling = .optimal,
.usage = usageFlags,
};
return img_create;
}
pub fn imageViewCreateInfo(
format: vk.Format,
image: vk.Image,
aspectFlags: vk.ImageAspectFlags,
mipLevel: u32,
) vk.ImageViewCreateInfo {
if (mipLevel == 0) {
core.engine_logs("create image view with mipLevel of 0");
}
const ivci = vk.ImageViewCreateInfo{
.flags = .{},
.image = image,
.view_type = .@"2d",
.format = format,
.components = .{ .r = .r, .g = .g, .b = .b, .a = .a },
.subresource_range = .{
.aspect_mask = aspectFlags,
.base_mip_level = 0,
.level_count = mipLevel,
.base_array_layer = 0,
.layer_count = 1,
},
};
return ivci;
}
pub fn samplerCreateInfo(
filters: vk.Filter,
samplerAddressMode: ?vk.SamplerAddressMode,
) vk.SamplerCreateInfo {
const addressMode = if (samplerAddressMode != null) samplerAddressMode.? else .repeat;
var self = vk.SamplerCreateInfo{
.flags = .{},
.mag_filter = filters,
.min_filter = .nearest,
.address_mode_u = addressMode,
.address_mode_v = addressMode,
.address_mode_w = addressMode,
.mipmap_mode = .nearest,
.mip_lod_bias = 0.0,
.anisotropy_enable = vk.FALSE,
.max_anisotropy = 0.0,
.compare_enable = vk.FALSE,
.compare_op = .never,
.min_lod = 0.0,
.max_lod = vk.LOD_CLAMP_NONE,
.border_color = .float_transparent_black,
.unnormalized_coordinates = vk.FALSE,
};
if (filters == .linear) {
self.max_lod = 4;
self.mipmap_mode = .linear;
}
return self;
}
pub fn writeDescriptorImage(
descriptorType: vk.DescriptorType,
dstSet: vk.DescriptorSet,
imageInfo: *vk.DescriptorImageInfo,
binding: u32,
) vk.WriteDescriptorSet {
const setWrite = vk.WriteDescriptorSet{
.dst_binding = binding,
.dst_set = dstSet,
.descriptor_count = 1,
.descriptor_type = descriptorType,
.p_buffer_info = undefined,
.dst_array_element = 0,
.p_image_info = @ptrCast(imageInfo),
.p_texel_buffer_view = undefined,
};
return setWrite;
}

View File

@ -1,388 +0,0 @@
const std = @import("std");
const vk = @import("vulkan");
const resources = @import("resources");
const core = @import("core");
const VkConstants = @import("vk_constants.zig");
const meshes = @import("mesh.zig");
const NeonVkContext = @import("vk_renderer.zig").NeonVkContext;
const assert = core.assert;
const NeonVkAllocator = @import("vk_allocator.zig").NeonVkAllocator;
pub const NeonVkMeshPushConstant = struct {
data: core.Vector4f,
render_matrix: core.Mat,
};
const DeviceDispatch = VkConstants.DeviceDispatch;
const BaseDispatch = VkConstants.BaseDispatch;
const InstanceDispatch = VkConstants.InstanceDispatch;
const ArrayList = std.ArrayList;
const Allocator = std.mem.Allocator;
const CStr = core.CStr;
const debug_struct = core.debug_struct;
pub fn default_pipeline_layout() vk.PipelineLayoutCreateInfo {
return vk.PipelineLayoutCreateInfo{
.flags = .{},
.set_layout_count = 0,
.p_set_layouts = undefined,
.push_constant_range_count = 0,
.p_push_constant_ranges = undefined,
};
}
fn make_depth_stencil_create_info(
depth_test: bool,
depth_write: bool,
compareOp: vk.CompareOp,
) vk.PipelineDepthStencilStateCreateInfo {
const pdsci = vk.PipelineDepthStencilStateCreateInfo{
.flags = .{},
.depth_test_enable = if (depth_test) vk.TRUE else vk.FALSE,
.depth_write_enable = if (depth_write) vk.TRUE else vk.FALSE,
.depth_compare_op = if (depth_test) compareOp else .never,
.depth_bounds_test_enable = vk.FALSE,
.min_depth_bounds = 0.0,
.max_depth_bounds = 1.0,
.stencil_test_enable = vk.FALSE,
.front = std.mem.zeroes(vk.StencilOpState),
.back = std.mem.zeroes(vk.StencilOpState),
};
return pdsci;
}
pub const NeonVkPipelineBuilder = struct {
vkd: DeviceDispatch,
allocator: Allocator,
vkAllocator: *NeonVkAllocator,
dev: vk.Device,
vertShaderModule: vk.ShaderModule,
fragShaderModule: vk.ShaderModule,
sscis: ArrayList(vk.PipelineShaderStageCreateInfo),
pvisci: vk.PipelineVertexInputStateCreateInfo,
piasci: vk.PipelineInputAssemblyStateCreateInfo,
prsci: vk.PipelineRasterizationStateCreateInfo,
pmsci: vk.PipelineMultisampleStateCreateInfo,
plci: ?vk.PipelineLayoutCreateInfo,
pdsci: ?vk.PipelineDepthStencilStateCreateInfo,
topology: vk.PrimitiveTopology = .triangle_list,
polygonMode: vk.PolygonMode = .fill,
pushConstantRange: ?vk.PushConstantRange,
viewport: vk.Viewport,
scissor: vk.Rect2D,
vertexInputDescription: ?meshes.VertexInputDescription,
colorBlendAttachment: vk.PipelineColorBlendAttachmentState,
pipelineLayout: vk.PipelineLayout,
descriptorLayouts: ArrayList(vk.DescriptorSetLayout),
pipelineName: []const u8 = "unknown",
// a seperate more convenient version of the default one
pub fn initFromContext(ctx: *NeonVkContext, vert_resource: anytype, frag_resource: anytype) !@This() {
return try NeonVkPipelineBuilder.init(
ctx.dev,
ctx.vkd,
ctx.allocator,
ctx.vkAllocator,
vert_resource.len,
@as([*]const u32, @ptrCast(@alignCast(&vert_resource))),
frag_resource.len,
@as([*]const u32, @ptrCast(@alignCast(&frag_resource))),
);
}
// call after all parameters are good to go.
pub fn build(self: *NeonVkPipelineBuilder, renderPass: vk.RenderPass) !?vk.Pipeline {
var pvsci = vk.PipelineViewportStateCreateInfo{
.flags = .{},
.viewport_count = 1,
.p_viewports = @ptrCast(&self.viewport),
.scissor_count = 1,
.p_scissors = @ptrCast(&self.scissor),
};
var pcbsci = vk.PipelineColorBlendStateCreateInfo{
.flags = .{},
.logic_op_enable = vk.FALSE,
.attachment_count = 1,
.p_attachments = @ptrCast(&self.colorBlendAttachment),
.logic_op = .copy,
.blend_constants = [4]f32{ 1.0, 1.0, 1.0, 1.0 },
};
var dynamicStates = [_]vk.DynamicState{
.viewport,
.scissor,
};
var dynamicStateCreateInfo = vk.PipelineDynamicStateCreateInfo{
.flags = .{},
.dynamic_state_count = 2,
.p_dynamic_states = &dynamicStates,
};
var gpci = vk.GraphicsPipelineCreateInfo{
.flags = .{},
.stage_count = @as(u32, @intCast(self.sscis.items.len)),
.p_stages = self.sscis.items.ptr,
.p_vertex_input_state = &self.pvisci, // : ?*const PipelineVertexInputStateCreateInfo,
.p_input_assembly_state = &self.piasci, //: ?*const PipelineInputAssemblyStateCreateInfo,
.p_tessellation_state = null, //: ?*const PipelineTessellationStateCreateInfo,
.p_viewport_state = &pvsci, //: ?*const PipelineViewportStateCreateInfo,
.p_rasterization_state = &self.prsci, //: *const PipelineRasterizationStateCreateInfo,
.p_multisample_state = &self.pmsci, //: ?*const PipelineMultisampleStateCreateInfo,
.p_depth_stencil_state = null, //: ?*const PipelineDepthStencilStateCreateInfo,
.p_color_blend_state = &pcbsci, //: ?*const PipelineColorBlendStateCreateInfo,
.p_dynamic_state = &dynamicStateCreateInfo, //: ?*const PipelineDynamicStateCreateInfo,
//.p_dynamic_state = null, //: ?*const PipelineDynamicStateCreateInfo,
.layout = self.pipelineLayout,
.render_pass = renderPass,
.subpass = 0,
.base_pipeline_handle = .null_handle,
.base_pipeline_index = 0,
};
if (self.pdsci != null) {
// core.graphics_logs("configuring with a valid set of stencil information");
gpci.p_depth_stencil_state = &(self.pdsci.?);
}
// debug_struct("building with pvisci: ", self.pvisci);
var pipeline: vk.Pipeline = undefined;
_ = try self.vkd.createGraphicsPipelines(self.dev, .null_handle, 1, @ptrCast(&gpci), null, @ptrCast(&pipeline));
return pipeline;
}
pub fn add_depth_stencil(self: *NeonVkPipelineBuilder) !void {
self.pdsci = make_depth_stencil_create_info(true, true, .less_or_equal);
}
// VkPipeline build_pipeline(VkDevice device, VkRenderPass pass);
pub fn init(
dev: vk.Device,
vkd: DeviceDispatch,
allocator: Allocator,
vkAllocator: *NeonVkAllocator,
vert_spv: []const u32,
frag_spv: []const u32,
) !@This() {
var self: NeonVkPipelineBuilder = undefined;
self.vkd = vkd;
self.allocator = allocator;
self.vkAllocator = vkAllocator;
self.dev = dev;
self.sscis = ArrayList(vk.PipelineShaderStageCreateInfo).init(allocator);
self.plci = null;
self.pushConstantRange = null;
self.pdsci = null;
self.descriptorLayouts = ArrayList(vk.DescriptorSetLayout).init(allocator);
self.topology = .triangle_list;
self.polygonMode = .fill;
self.vertShaderModule = try self.vkd.createShaderModule(self.dev, &.{
.flags = .{},
.code_size = vert_spv.len * 4,
.p_code = vert_spv.ptr,
}, null);
self.fragShaderModule = try self.vkd.createShaderModule(self.dev, &.{
.flags = .{},
.code_size = frag_spv.len * 4,
.p_code = frag_spv.ptr,
}, null);
self.vertexInputDescription = null;
return self;
}
pub fn add_layout(self: *NeonVkPipelineBuilder, layout: vk.DescriptorSetLayout) !void {
if (self.plci == null) {
self.plci = default_pipeline_layout();
try assert(self.descriptorLayouts.items.len == 0);
}
try self.descriptorLayouts.append(layout);
self.plci.?.set_layout_count += 1;
self.plci.?.p_set_layouts = self.descriptorLayouts.items.ptr;
}
pub fn add_push_constant_custom(self: *NeonVkPipelineBuilder, comptime PushConstant: type) !void {
if (self.plci == null) {
self.plci = default_pipeline_layout();
}
self.pushConstantRange = vk.PushConstantRange{
.offset = 0,
.size = @sizeOf(PushConstant),
.stage_flags = .{ .vertex_bit = true, .fragment_bit = true },
};
self.plci.?.push_constant_range_count = 1;
self.plci.?.p_push_constant_ranges = @ptrCast(&(self.pushConstantRange.?));
}
pub fn add_push_constant(self: *NeonVkPipelineBuilder) !void {
if (self.plci == null) {
self.plci = default_pipeline_layout();
}
self.pushConstantRange = vk.PushConstantRange{
.offset = 0,
.size = @sizeOf(NeonVkMeshPushConstant),
.stage_flags = .{ .vertex_bit = true },
};
self.plci.?.push_constant_range_count = 1;
self.plci.?.p_push_constant_ranges = @ptrCast(&(self.pushConstantRange.?));
}
pub fn add_mesh_description(self: *NeonVkPipelineBuilder) !void {
// core.graphics_logs("adding vertex mesh description");
self.vertexInputDescription = try meshes.VertexInputDescription.init(self.allocator);
}
pub fn set_topology(self: *@This(), topology: vk.PrimitiveTopology) void {
self.topology = topology;
}
pub fn set_polygon_mode(self: *@This(), polygonMode: vk.PolygonMode) void {
self.polygonMode = polygonMode;
}
// the init _ functions are called last and perform cleanup. all the other add_ functions can be called
// before this
pub fn init_triangle_pipeline(self: *NeonVkPipelineBuilder, extents: vk.Extent2D) !void {
try self.add_shader_stage(.{ .vertex_bit = true }, self.vertShaderModule);
try self.add_shader_stage(.{ .fragment_bit = true }, self.fragShaderModule);
self.pvisci = vk.PipelineVertexInputStateCreateInfo{
.flags = .{},
.vertex_binding_description_count = 0,
.vertex_attribute_description_count = 0,
.p_vertex_attribute_descriptions = undefined,
.p_vertex_binding_descriptions = undefined,
};
if (self.vertexInputDescription != null) {
const desc = self.vertexInputDescription.?;
self.pvisci.vertex_attribute_description_count = @as(u32, @intCast(desc.attributes.items.len));
self.pvisci.p_vertex_attribute_descriptions = desc.attributes.items.ptr;
self.pvisci.vertex_binding_description_count = @as(u32, @intCast(desc.bindings.items.len));
self.pvisci.p_vertex_binding_descriptions = desc.bindings.items.ptr;
// core.graphics_logs("setting up vertex description");
}
self.piasci = vk.PipelineInputAssemblyStateCreateInfo{
.flags = .{},
.topology = self.topology,
// .topology = .line_list,
.primitive_restart_enable = vk.FALSE,
};
self.prsci = .{
.flags = .{},
.depth_clamp_enable = vk.FALSE,
.rasterizer_discard_enable = vk.FALSE,
.polygon_mode = self.polygonMode,
//.polygon_mode = .line,
//.cull_mode = .{ .back_bit = true },
.cull_mode = .{ .back_bit = false },
.front_face = .clockwise,
.depth_bias_enable = vk.FALSE,
.depth_bias_constant_factor = 0.0,
.depth_bias_clamp = 0.0,
.depth_bias_slope_factor = 0.0,
.line_width = 1.0,
}; // rasterizer settings
self.pmsci = .{
.flags = .{},
.rasterization_samples = .{ .@"1_bit" = true },
.min_sample_shading = 1.0,
.sample_shading_enable = vk.FALSE,
.p_sample_mask = null,
.alpha_to_coverage_enable = vk.FALSE,
.alpha_to_one_enable = vk.FALSE,
}; // multisampling settings
self.viewport = vk.Viewport{
.x = 0,
.y = 0,
.width = @as(f32, @floatFromInt(extents.width)),
.height = @as(f32, @floatFromInt(extents.height)),
.min_depth = 0.0,
.max_depth = 1.0,
};
self.scissor = .{
.offset = .{ .x = 0, .y = 0 },
.extent = extents,
};
self.colorBlendAttachment = .{
.blend_enable = vk.TRUE,
.src_color_blend_factor = .src_alpha,
.dst_color_blend_factor = .one_minus_src_alpha,
.src_alpha_blend_factor = .src_alpha,
.dst_alpha_blend_factor = .one_minus_src_alpha,
.alpha_blend_op = .add,
.color_write_mask = .{
.r_bit = true,
.g_bit = true,
.b_bit = true,
.a_bit = true,
},
.color_blend_op = .add,
}; //
if (self.plci == null)
self.plci = default_pipeline_layout();
//self.pipelineLayout = try self.vkd.createPipelineLayout(self.dev, &(self.plci.?), null);
self.pipelineLayout = try self.vkAllocator.createPipelineLayout(self.dev, self.plci.?, "triangle pipeline");
}
pub fn add_shader_stage(
self: *NeonVkPipelineBuilder,
stageFlags: vk.ShaderStageFlags,
shaderModule: vk.ShaderModule,
) !void {
const info = vk.PipelineShaderStageCreateInfo{
.flags = .{},
.stage = stageFlags,
.module = shaderModule,
.p_name = "main",
.p_specialization_info = null,
};
try self.sscis.append(info);
}
pub fn deinit(self: *NeonVkPipelineBuilder) void {
self.vkd.destroyShaderModule(self.dev, self.fragShaderModule, null);
self.vkd.destroyShaderModule(self.dev, self.vertShaderModule, null);
self.sscis.deinit();
if (self.vertexInputDescription != null)
self.vertexInputDescription.?.deinit();
self.descriptorLayouts.deinit();
}
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,174 +0,0 @@
pub fn MakeCubeMapList(
comptime left: []const u8,
comptime up: []const u8,
comptime down: []const u8,
comptime front: []const u8,
comptime back: []const u8,
) []const []const u8 {
return &.{
left,
up,
down,
front,
back,
};
}
pub const CubeMapDirs = enum(u8) {
right,
left,
up,
down,
front,
back,
};
const vk_utils = @import("../vk_utils.zig");
const LoadAndStageImage = vk_utils.LoadAndStageImage;
pub fn stageCubeTexture(list: []const []const u8) !LoadAndStageImage {
try core.assert(list.len == 6);
const gc = graphics.getContext();
const allocator = gc.allocator;
var pngs: [6]core.png.PngContents = undefined;
for (0..6) |i| {
pngs[i] = try core.png.PngContents.initFromPathSpec(list[i], allocator);
}
defer {
for (&pngs) |*png| {
png.deinit();
}
}
const width = pngs[0].size.x;
const height = pngs[0].size.y;
try core.assert(width == height);
core.engine_log("cubemap dimensionss {d}x{d}", .{ width, height });
var totalLen: u32 = 0;
for (pngs) |png| {
try core.assertf(width == png.size.x, "inconsistent cubemap dimensions", .{});
try core.assertf(height == png.size.y, "inconsistent cubemap dimensions", .{});
totalLen += @intCast(png.pixels.len);
}
const stagingBuffer = try gc.vkAllocator.createStagingBuffer(totalLen, "cubemap creation staging texture map");
const pixelBuffer = try gc.vkAllocator.mapBuffer(u8, stagingBuffer);
var offset: u32 = 0;
var bufferOffsets: [6]u32 = undefined;
for (pngs, 0..) |png, i| {
const dest = pixelBuffer[offset .. offset + png.pixels.len];
bufferOffsets[i] = offset;
offset += @intCast(png.pixels.len);
@memcpy(dest, png.pixels);
}
const imageExtent = vk.Extent3D{
.width = @as(u32, @intCast(width)),
.height = @as(u32, @intCast(height)),
.depth = 1,
};
//const mipLevel = std.math.log2(@max(imageExtent.width, imageExtent.height)) + 1;
const mipLevel = 1;
var imgCreateInfo = vkinit.imageCreateInfo(.r8g8b8a8_srgb, .{
.sampled_bit = true,
.transfer_dst_bit = true,
}, imageExtent, mipLevel);
imgCreateInfo.array_layers = 6;
imgCreateInfo.flags.cube_compatible_bit = true;
if (mipLevel > 1) {
imgCreateInfo.usage.transfer_src_bit = true;
}
const imgAllocInfo = vma.AllocationCreateInfo{
.requiredFlags = .{},
.usage = .gpuOnly,
};
const newImage = try gc.vkAllocator.createImage(imgCreateInfo, imgAllocInfo, "cubemap creation image");
gc.vkAllocator.unmapMemory(stagingBuffer);
return .{
.stagingBuffer = stagingBuffer,
.image = newImage,
.mipLevel = mipLevel,
.cubeOffsets = bufferOffsets,
};
}
pub fn submitTextureCube(uploader: *vk_utils.NeonVkUploader, state: *const LoadAndStageImage) !void {
try core.assert(state.cubeOffsets != null);
if (state.cubeOffsets) |cubeOffsets| {
try uploader.startUploadContext();
{
const newImage = state.image;
const mipLevel = state.mipLevel;
const cmd = uploader.commandBuffer;
transitions.into_transferDst(cmd, newImage.image, mipLevel, 0, 6);
for (cubeOffsets, 0..) |offset, face| {
var copyRegion = vk.BufferImageCopy{
.buffer_offset = offset,
.buffer_row_length = 0,
.buffer_image_height = 0,
.image_offset = std.mem.zeroes(vk.Offset3D),
.image_subresource = .{
.aspect_mask = .{ .color_bit = true },
.mip_level = 0,
.base_array_layer = @intCast(face),
.layer_count = 1,
},
.image_extent = .{
.width = newImage.pixelWidth,
.height = newImage.pixelHeight,
.depth = 1,
},
};
vkd.cmdCopyBufferToImage(
cmd,
state.stagingBuffer.buffer,
newImage.image,
.transfer_dst_optimal,
1,
@ptrCast(&copyRegion),
);
try vk_utils.generateMipMaps(cmd, newImage, mipLevel, 0);
}
transitions.transferDst_into_shaderReadOnly(cmd, newImage.image, mipLevel, 0, 6);
}
try uploader.finishUploadContext();
}
}
pub fn createDescriptorSet(
dev: vk.Device,
) struct {
layout: vk.DescriptorSetLayout,
descriptorSet: vk.DescriptorSet,
} {
_ = dev;
}
const core = @import("core");
const vk_renderer = @import("../vk_renderer.zig");
const vma = @import("vma");
const graphics = @import("../graphics.zig");
const vk = @import("vulkan");
const vkinit = @import("../vk_init.zig");
const vk_constants = @import("../vk_constants.zig");
const std = @import("std");
const vkd = vk_api.vkd;
const vk_api = @import("../vk_api.zig");
const transitions = @import("../vk_transitions.zig");

View File

@ -1,821 +0,0 @@
const std = @import("std");
const vk = @import("vulkan");
const core = @import("core");
const zgltf = core.zgltf;
pub const MeshPoolCreationSettings = struct {
vertexCount: u32 = 4_000_000,
indexCount: u32 = 16_000_000,
};
pub const MeshUpdate = union(enum(u8)) {
new: struct {
vertices: []MeshVertex,
indices: []u32,
jointNames: []JointNameEntry,
name: core.Name,
skeletonName: ?core.Name,
},
free: struct {
vertices: Span,
indices: Span,
name: core.Name,
},
pub fn deinit(self: @This(), allocator: std.mem.Allocator) void {
switch (self) {
.new => |new| {
allocator.free(new.vertices);
allocator.free(new.indices);
for (new.jointNames) |entry| {
entry.deinit();
}
allocator.free(new.jointNames);
},
.free => {},
}
}
};
const UploadList = struct {
ctx: *MeshPoolBuffers,
uploads: std.ArrayList(Transfer),
destination: NeonVkBuffer,
pub const Transfer = struct {
staging: NeonVkBuffer,
destination: Span,
};
pub fn init(ctx: *MeshPoolBuffers, destination: NeonVkBuffer) @This() {
return .{
.ctx = ctx,
.uploads = std.ArrayList(Transfer).init(ctx.allocator),
.destination = destination,
};
}
pub fn issueCopy(self: *@This(), uploader: *NeonVkUploader, index: u32, elementSize: u32) !void {
try core.assert(uploader.isActive);
const upload = self.uploads.items[index];
var copy = vk.BufferCopy{
.dst_offset = upload.destination.start * elementSize,
.src_offset = 0,
.size = upload.destination.size * elementSize,
};
const cmd = uploader.commandBuffer;
vkd.cmdCopyBuffer(
cmd,
upload.staging.buffer,
self.destination.buffer,
1,
@as([*]const vk.BufferCopy, @ptrCast(&copy)),
);
}
pub fn deinit(self: *@This()) void {
for (self.uploads.items) |*up| {
up.staging.deinit(self.ctx.vkAllocator);
}
self.uploads.deinit();
}
};
// should be owned by renderthread
//
// operation per frame
//
// 1. async loading of vertices push model load results into a queue
// 2. these results ar ethen installed into the vertex pool
var gMeshPoolBuffer: *MeshPoolBuffers = undefined;
pub fn getMeshPoolAllocator() std.mem.Allocator {
return gMeshPoolBuffer.allocator;
}
const MeshVertexTransmute = extern struct { data: [@sizeOf(MeshVertex)]u8 };
pub const IndexedMesh = struct {
vertex: Span,
index: Span,
name: core.Name,
jointRemap: ?[]u8, // this is NOT a string, they're joint indices.. which happen to be u8s
};
pub fn getIndexedMeshByName(_name: core.Name) ?IndexedMesh {
var name = _name;
gMeshPoolBuffer.vertexMapLock.lock();
defer gMeshPoolBuffer.vertexMapLock.unlock();
return gMeshPoolBuffer.vertexMap.get(name.handle());
}
pub const MeshPoolBuffers = struct {
vertexBuffer: NeonVkBuffer, // gpu sided vertex buffer
indexBuffer: NeonVkBuffer, // gpu sided vertex buffer
allocator: std.mem.Allocator,
vkAllocator: *NeonVkAllocator,
gc: *NeonVkContext,
uploader: NeonVkUploader,
updateRequests: Requests,
indexSpans: MergedSpans,
vertexSpans: MergedSpans,
vertexMapLock: std.Thread.Mutex,
vertexMap: std.AutoHashMapUnmanaged(u32, IndexedMesh),
jointMaps: std.AutoHashMapUnmanaged(u32, JointMapEntry),
const JointMapEntry = std.AutoHashMapUnmanaged(u32, u32);
const Requests = core.RingQueue(MeshUpdate);
pub fn create(
allocator: std.mem.Allocator,
gc: *NeonVkContext,
opt: MeshPoolCreationSettings,
) !*@This() {
const self = try allocator.create(@This());
self.allocator = allocator;
self.updateRequests = try Requests.init(allocator, 4096);
self.vertexMap = .{};
self.vertexMapLock = .{};
self.jointMaps = .{};
self.indexSpans = try MergedSpans.init(allocator, opt.indexCount);
self.vertexSpans = try MergedSpans.init(allocator, opt.vertexCount);
self.vertexBuffer = try gc.vkAllocator.createGpuBuffer(opt.vertexCount * @sizeOf(MeshVertex), .{
.vertex_buffer_bit = true,
}, "Mesh Pool gpu vertex buffer");
self.indexBuffer = try gc.vkAllocator.createGpuBuffer(opt.indexCount * @sizeOf(u32), .{
.index_buffer_bit = true,
}, "Mesh Pool gpu vertex buffer");
self.gc = gc;
self.vkAllocator = gc.vkAllocator;
self.uploader = try NeonVkUploader.init(gc, "Mesh Pool uploader");
gMeshPoolBuffer = self;
return self;
}
pub fn checkUpdates(self: *@This()) !void {
if (self.updateRequests.count() <= 0) {
return;
}
self.updateRequests.lock();
defer self.updateRequests.unlock();
var vertexUploadList = UploadList.init(self, self.vertexBuffer);
defer vertexUploadList.deinit();
var indexUploadList = UploadList.init(self, self.indexBuffer);
defer indexUploadList.deinit();
while (self.updateRequests.popFromUnlocked()) |update| {
switch (update) {
.new => |new| {
const indexSpan = try self.indexSpans.allocate(@intCast(new.indices.len));
const vertexSpan = try self.vertexSpans.allocate(@intCast(new.vertices.len));
const stagingVertex = try self.vkAllocator.createStagingBuffer(
@intCast(new.vertices.len * @sizeOf(MeshVertex)),
"staging vertex buffer",
);
{
const stagingVertexMapped = try self.vkAllocator.mapBuffer(MeshVertex, stagingVertex);
defer self.vkAllocator.unmapMemory(stagingVertex);
std.mem.copyForwards(MeshVertex, stagingVertexMapped, new.vertices);
}
const stagingIndex = try self.vkAllocator.createStagingBuffer(
@intCast(new.indices.len * @sizeOf(u32)),
"staging index buffer",
);
{
const stagingMapped = try self.vkAllocator.mapBuffer(u32, stagingIndex);
defer self.vkAllocator.unmapMemory(stagingIndex);
for (new.indices, 0..) |index, i| {
stagingMapped[i] = index + vertexSpan.start;
}
}
try vertexUploadList.uploads.append(.{ .staging = stagingVertex, .destination = vertexSpan });
try indexUploadList.uploads.append(.{ .staging = stagingIndex, .destination = indexSpan });
var jointMap: JointMapEntry = .{};
for (new.jointNames) |entry| {
var entryName = core.MakeName(entry.name);
// core.engine_log("gtlf bone found {s} -> {d}", .{ entry.name, entry.index });
try jointMap.put(self.allocator, entryName.handle(), entry.index);
}
var newName = new.name;
try gMeshPoolBuffer.jointMaps.put(self.allocator, newName.handle(), jointMap);
var jointRemap: ?[]u8 = null;
if (new.skeletonName) |skName| {
if (animationSystem.getSkeletonByName(skName)) |sk| {
// build the joint remap
// this is a map from ozz's index to gltf's index
var iter = sk.jointMapping.iterator();
jointRemap = try graphics.getContext().allocator.alloc(u8, sk.jointMapping.count());
while (iter.next()) |i| {
const jointName = i.key_ptr.*;
const ozzIndex = i.value_ptr.*;
var jn = core.MakeName(jointName);
var gltfIndex = jointMap.get(jn.handle());
if (gltfIndex == null) {
gltfIndex = 0;
// core.engine_log("ERROR REMAPPING BONE setting to zero {s}", .{jointName});
}
// core.engine_log("remapping bone from {s} ozz {d} -> {d} gltf", .{ jointName, ozzIndex, gltfIndex.? });
jointRemap.?[ozzIndex] = @intCast(gltfIndex.?);
}
}
}
gMeshPoolBuffer.vertexMapLock.lock();
try gMeshPoolBuffer.vertexMap.put(self.allocator, newName.handle(), .{ .index = indexSpan, .vertex = vertexSpan, .name = newName, .jointRemap = jointRemap });
gMeshPoolBuffer.vertexMapLock.unlock();
},
.free => |free| {
self.vertexSpans.removeSpan(free.vertices);
self.indexSpans.removeSpan(free.indices);
},
}
update.deinit(self.allocator);
}
try self.uploader.startUploadContext();
// iterate over both upload lists and isssue uploads.
for (indexUploadList.uploads.items, 0..) |_, i| {
try indexUploadList.issueCopy(&self.uploader, @intCast(i), @sizeOf(u32));
try vertexUploadList.issueCopy(&self.uploader, @intCast(i), @sizeOf(MeshVertex));
}
// Insert Barrier for indexBuffer
var indexMemoryBarrier = vk.BufferMemoryBarrier{
.buffer = self.indexBuffer.buffer,
.src_access_mask = .{ .transfer_read_bit = true },
.dst_access_mask = .{ .index_read_bit = true },
.src_queue_family_index = 0,
.dst_queue_family_index = 0,
.offset = 0,
.size = vk.WHOLE_SIZE,
};
vkd.cmdPipelineBarrier(
self.uploader.commandBuffer, //
.{ .transfer_bit = true }, //
.{ .vertex_input_bit = true }, //
.{}, //
0,
undefined,
1,
@ptrCast(&indexMemoryBarrier),
0,
undefined,
);
// Insert Barrier for vertexBuffer
var vertexMemoryBarrier = vk.BufferMemoryBarrier{
.buffer = self.vertexBuffer.buffer,
.src_access_mask = .{ .transfer_read_bit = true },
.dst_access_mask = .{
.vertex_attribute_read_bit = true,
},
.src_queue_family_index = 0,
.dst_queue_family_index = 0,
.offset = 0,
.size = vk.WHOLE_SIZE,
};
vkd.cmdPipelineBarrier(
self.uploader.commandBuffer,
.{ .transfer_bit = true },
.{ .vertex_input_bit = true },
.{},
0,
undefined,
1,
@ptrCast(&vertexMemoryBarrier),
0,
undefined,
);
try self.uploader.finishUploadContext();
}
pub fn destroy(self: *@This()) void {
{
self.updateRequests.lock();
defer self.updateRequests.unlock();
while (self.updateRequests.popFromUnlocked()) |x| {
x.deinit(self.allocator);
}
}
{
var iter = self.vertexMap.valueIterator();
while (iter.next()) |p| {
if (p.jointRemap) |jr| {
graphics.getContext().allocator.free(jr);
}
}
}
self.vertexMap.deinit(self.allocator);
self.indexSpans.deinit();
self.vertexSpans.deinit();
{
var iter = self.jointMaps.valueIterator();
while (iter.next()) |p| {
p.deinit(self.allocator);
}
}
self.jointMaps.deinit(self.allocator);
self.vertexBuffer.deinit(self.gc.vkAllocator);
self.indexBuffer.deinit(self.gc.vkAllocator);
self.uploader.deinit();
self.updateRequests.deinit();
self.allocator.destroy(self);
}
};
pub const PoolMesh = struct {
vertexSpan: Span,
indexSpan: Span,
};
pub fn getMeshPoolBuffers() struct { index: NeonVkBuffer, vertex: NeonVkBuffer } {
return .{
.index = gMeshPoolBuffer.indexBuffer,
.vertex = gMeshPoolBuffer.vertexBuffer,
};
}
pub const MeshSourceType = enum { obj, gltf };
pub const LoadMeshSettings = struct {
path: []const u8,
sourceType: ?MeshSourceType = null,
skeletonName: ?core.Name,
};
pub fn loadIndexedMeshForPooling(meshName: core.Name, opt: LoadMeshSettings) !void {
var sourceType = MeshSourceType.gltf;
if (opt.sourceType) |st| {
sourceType = st;
}
// check if we have a cooked version of that file, if so just load that instead.
// otherwise, load the file
switch (sourceType) {
.obj => {
try loadIndexedMeshForPoolingObj(meshName, opt.path);
},
.gltf => {
try loadIndexedMeshForPoolingGltf(meshName, opt.skeletonName, opt.path);
},
}
}
pub fn loadIndexedMeshForPoolingGltf(meshName: core.Name, skeletonName: ?core.Name, path: []const u8) !void {
const file = try core.fs().loadFile(path);
defer core.fs().unmap(file);
const allocator = gMeshPoolBuffer.allocator;
var parser = zgltf.init(allocator);
defer parser.deinit();
const ext = core.getFileExtension(path);
if (std.mem.eql(u8, ".gltf", ext)) {
try parser.parse(@alignCast(file.bytes[0 .. file.bytes.len - 1]));
} else {
try parser.parse(@alignCast(file.bytes));
}
// std.debug.print("\n", .{});
// parser.debugPrint();
if (parser.data.meshes.items.len > 1) {
return error.OnlyOneMeshPerGltfImplemented;
}
if (parser.data.skins.items.len > 1) {
return error.TooManySkins;
}
var binaryFile: ?core.packer.PackerBytesMapping = null;
var binaryBytes: []const u8 = undefined;
if (std.mem.eql(u8, ext, ".glb")) {
binaryBytes = parser.glb_binary.?;
} else {
const binaryPath = try std.fmt.allocPrint(allocator, "{s}bin", .{path[0 .. path.len - 4]});
defer allocator.free(binaryPath);
core.engine_log("{s}", .{binaryPath});
binaryFile = try core.fs().loadFile(binaryPath);
binaryBytes = binaryFile.?.bytes;
}
defer if (binaryFile) |f| core.fs().unmap(f);
const m = parser.data.meshes.items[0];
core.engine_log("mesh name {s} number of primitives = {d}", .{ m.name, m.primitives.items.len });
var positions = std.ArrayList(f32).init(allocator);
defer positions.deinit();
var texcoords = std.ArrayList(f32).init(allocator);
defer texcoords.deinit();
var normals = std.ArrayList(f32).init(allocator);
defer normals.deinit();
var joints = std.ArrayList(u16).init(allocator);
defer joints.deinit();
// add a different joint format one, todo- i need to fix up zgltf
var useJoints8: bool = false;
var joints8 = std.ArrayList(u8).init(allocator);
defer joints8.deinit();
var weights = std.ArrayList(f32).init(allocator);
defer weights.deinit();
var weightCount: usize = 4;
if (m.primitives.items.len > 1) {
@panic("sorry, havent implemented support for multiple primitives yet, would require more work on the way i handle materials");
}
var indexList = std.ArrayList(u32).init(allocator);
for (m.primitives.items) |primitive| {
if (primitive.indices) |indices| {
const accessor = parser.data.accessors.items[indices];
// core.engine_log("index accessor info: {any}", .{accessor});
if (accessor.component_type == .unsigned_short) {
var temp = std.ArrayList(u16).init(allocator);
defer temp.deinit();
parser.getDataFromBufferView(u16, &temp, accessor, @alignCast(binaryBytes));
for (temp.items) |t| {
try indexList.append(@intCast(t));
}
} else if (accessor.component_type == .unsigned_integer) {
parser.getDataFromBufferView(u32, &indexList, accessor, @alignCast(binaryBytes));
}
}
for (primitive.attributes.items) |attribute| {
// core.engine_log("attribute: {any}", .{attribute});
switch (attribute) {
.position => |x| {
const accessor = parser.data.accessors.items[x];
// core.engine_log("accessor info: {any}", .{accessor});
parser.getDataFromBufferView(f32, &positions, accessor, @alignCast(binaryBytes));
// core.engine_log("positions loaded: {d}", .{positions.items.len});
},
.normal => |x| {
const accessor = parser.data.accessors.items[x];
// core.engine_log("accessor info: {any}", .{accessor});
parser.getDataFromBufferView(f32, &normals, accessor, @alignCast(binaryBytes));
// core.engine_log("normals loaded: {d}", .{normals.items.len});
},
.texcoord => |x| {
const accessor = parser.data.accessors.items[x];
// core.engine_log("accessor info: {any}", .{accessor});
parser.getDataFromBufferView(f32, &texcoords, accessor, @alignCast(binaryBytes));
// core.engine_log("texcoords loaded: {d}", .{texcoords.items.len});
},
.joints => |x| {
const accessor = parser.data.accessors.items[x];
// core.engine_log("accessor info: {any} acecssor index {d}", .{ accessor, x });
if (accessor.component_type == .unsigned_byte) {
useJoints8 = true;
parser.getDataFromBufferView(u8, &joints8, accessor, @alignCast(binaryBytes));
// core.engine_log("joints8 loaded: {d} - {d} {d} {d} {d}", .{ joints8.items.len, joints8.items[0], joints8.items[1], joints8.items[2], joints8.items[3] });
} else {
parser.getDataFromBufferView(u16, &joints, accessor, @alignCast(binaryBytes));
// core.engine_log("joints loaded: {d} - {d} {d} {d} {d}", .{ joints.items.len, joints.items[0], joints.items[1], joints.items[2], joints.items[3] });
}
},
.weights => |x| {
const accessor = parser.data.accessors.items[x];
// core.engine_log("accessor info: {any}", .{accessor});
parser.getDataFromBufferView(f32, &weights, accessor, @alignCast(binaryBytes));
if (accessor.type == .vec3) {
weightCount = 3;
}
// core.engine_log("weights loaded: {d} - {d} {d} {d} {d}", .{ weights.items.len, weights.items[0], weights.items[1], weights.items[2], weights.items[3] });
},
.tangent => |x| {
const accessor = parser.data.accessors.items[x];
core.engine_log("accessor info: {any} NOT PARSED", .{accessor});
},
.color => |x| {
const accessor = parser.data.accessors.items[x];
core.engine_log("accessor info: {any} NOT PARSED", .{accessor});
},
}
}
}
if (parser.data.skins.items.len > 1) {
@panic("too many skins, not supported");
}
var jointNameList: std.ArrayList(JointNameEntry) = std.ArrayList(JointNameEntry).init(allocator);
if (weights.items.len > 0) {
core.engine_log("skin found, building joint map", .{});
if (parser.data.skins.items[0].skeleton) |skeletonIndex| {
for (parser.data.nodes.items[skeletonIndex..], 0..) |node, i| {
// core.engine_log("gltf: {s} -> {d} (skeleton index)", .{ node.name, i });
const gcAllocator = graphics.getContext().allocator;
try jointNameList.append(.{ .index = @intCast(i), .name = try gcAllocator.dupe(u8, node.name) });
}
} else {
if (parser.data.skins.items[0].joints.items.len > 0) {
for (parser.data.skins.items[0].joints.items, 0..) |i, j| {
const node = parser.data.nodes.items[i];
// core.engine_log("gltf: {s} -> {d} (joints map)", .{ node.name, j });
const gcAllocator = graphics.getContext().allocator;
try jointNameList.append(.{ .index = @intCast(j), .name = try gcAllocator.dupe(u8, node.name) });
}
} else {
for (parser.data.nodes.items, 0..) |node, i| {
// core.engine_log("gltf: {s} -> {d} (fallback)", .{ node.name, i });
const gcAllocator = graphics.getContext().allocator;
try jointNameList.append(.{ .index = @intCast(i), .name = try gcAllocator.dupe(u8, node.name) });
}
}
}
}
var vertexList = std.ArrayList(MeshVertex).init(allocator);
var i: usize = 0;
const vertexCount = positions.items.len / 3;
while (i < vertexCount) : (i += 1) {
const normalIndex = i * 3;
const positionIndex = i * 3;
const uvIndex = i * 2;
const uv: core.Vector2f = if (uvIndex < texcoords.items.len) .{
.x = texcoords.items[uvIndex],
.y = texcoords.items[uvIndex + 1],
} else core.Vector2f{};
const normal = if (normalIndex < normals.items.len) core.Vectorf{
.x = normals.items[i],
.y = normals.items[i + 1],
.z = normals.items[i + 2],
} else core.Vectorf{};
try vertexList.append(.{
.position = .{
.x = positions.items[positionIndex],
.y = positions.items[positionIndex + 1],
.z = positions.items[positionIndex + 2],
},
.normal = normal,
.color = .{},
.uv = uv,
});
const jointsIndex = weightCount * i;
if (weightCount == 4) {
if (useJoints8) {
if (jointsIndex < joints8.items.len) {
vertexList.items[vertexList.items.len - 1].bones = .{
@intCast(joints8.items[jointsIndex + 0]),
@intCast(joints8.items[jointsIndex + 1]),
@intCast(joints8.items[jointsIndex + 2]),
@intCast(joints8.items[jointsIndex + 3]),
};
vertexList.items[vertexList.items.len - 1].weights = .{
@intFromFloat(weights.items[jointsIndex + 0] * 255),
@intFromFloat(weights.items[jointsIndex + 1] * 255),
@intFromFloat(weights.items[jointsIndex + 2] * 255),
@intFromFloat(weights.items[jointsIndex + 3] * 255),
};
}
} else {
if (jointsIndex < joints.items.len) {
vertexList.items[vertexList.items.len - 1].bones = .{
@intCast(joints.items[jointsIndex + 0]),
@intCast(joints.items[jointsIndex + 1]),
@intCast(joints.items[jointsIndex + 2]),
@intCast(joints.items[jointsIndex + 3]),
};
vertexList.items[vertexList.items.len - 1].weights = .{
@intFromFloat(weights.items[jointsIndex + 0] * 255),
@intFromFloat(weights.items[jointsIndex + 1] * 255),
@intFromFloat(weights.items[jointsIndex + 2] * 255),
@intFromFloat(weights.items[jointsIndex + 3] * 255),
};
}
}
} else {
return error.NotImplementedYet;
}
}
if (indexList.items.len == 0) {
for (0..vertexList.items.len) |x| {
try indexList.append(@intCast(x));
}
}
const rv: MeshUpdate = .{
.new = .{
.vertices = try vertexList.toOwnedSlice(),
.indices = try indexList.toOwnedSlice(),
.jointNames = try jointNameList.toOwnedSlice(),
.skeletonName = skeletonName,
.name = meshName,
},
};
core.graphics_log("[{s}] gltf loaded vertex count vertices={d} indices={d}", .{ path, rv.new.vertices.len, rv.new.indices.len });
try gMeshPoolBuffer.updateRequests.pushLocked(rv);
// return error.NotImplementedYet;
// var vertexList = std.ArrayList(MeshVertex).init(allocator);
// var indexList = std.ArrayList(u32).init(allocator);
// const rv: MeshUpdate = .{
// .new = .{
// .vertices = try vertexList.toOwnedSlice(),
// .indices = try indexList.toOwnedSlice(),
// .name = meshName,
// },
// };
// core.graphics_log("[{s}] vertex count vertices={d} indices={d}", .{ path, rv.new.vertices.len, rv.new.indices.len });
// try gMeshPoolBuffer.updateRequests.pushLocked(rv);
}
pub fn pushMeshUpdateRequest(update: MeshUpdate) !void {
try gMeshPoolBuffer.updateRequests.pushLocked(update);
}
pub fn loadIndexedMeshForPoolingObj(meshName: core.Name, path: []const u8) !void {
const file = try core.fs().loadFile(path);
defer core.fs().unmap(file);
const allocator = gMeshPoolBuffer.allocator;
var Objs = try objLoader.loadObjBytes(file.bytes, allocator);
defer Objs.deinit();
var vertexMap = std.AutoHashMap(MeshVertexTransmute, u32).init(allocator);
defer vertexMap.deinit();
var vertexList = std.ArrayList(MeshVertex).init(allocator);
var indexList = std.ArrayList(u32).init(allocator);
const m: *objLoader.ObjMesh = &Objs.meshes.items[0];
// only thing i care about right now is normal and position
for (m.v_faces.items) |f| {
const face: objLoader.ObjFace = f;
if (face.count == 3) {
for (0..face.count) |i| {
const p = m.v_positions.items[face.vertex[i] - 1];
const n = m.v_normals.items[face.normal[i] - 1];
const u = m.v_uvs.items[face.texture[i] - 1];
const meshVertex: MeshVertex = .{
.position = .{ .x = p.x, .y = p.y, .z = p.z },
.normal = .{ .x = n.x, .y = n.y, .z = n.z },
.color = .{ .r = n.x, .g = n.y, .b = n.z, .a = 1.0 },
.uv = .{ .x = u.x, .y = 1 - u.y },
};
var index: u32 = @intCast(vertexList.items.len);
const transmute: MeshVertexTransmute = @bitCast(meshVertex);
if (vertexMap.get(transmute)) |cachedIndex| {
index = cachedIndex;
} else {
try vertexMap.put(transmute, index);
try vertexList.append(meshVertex);
}
try indexList.append(index);
}
}
if (face.count == 4) {
const il: []const usize = &.{ 0, 1, 2, 2, 3, 0 };
for (il) |i| {
const p = m.v_positions.items[face.vertex[i] - 1];
const n = m.v_normals.items[face.normal[i] - 1];
const u = m.v_uvs.items[face.texture[i] - 1];
const meshVertex: MeshVertex = .{
.position = .{ .x = p.x, .y = p.y, .z = p.z },
.normal = .{ .x = n.x, .y = n.y, .z = n.z },
.color = .{ .r = n.x, .g = n.y, .b = n.z, .a = 1.0 },
.uv = .{ .x = u.x, .y = 1 - u.y },
};
var index: u32 = @intCast(vertexList.items.len);
const transmute: MeshVertexTransmute = @bitCast(meshVertex);
if (vertexMap.get(transmute)) |cachedIndex| {
index = cachedIndex;
} else {
try vertexMap.put(transmute, index);
try vertexList.append(meshVertex);
}
try indexList.append(index);
}
}
}
var jointNames = std.ArrayList(JointNameEntry).init(allocator);
const rv: MeshUpdate = .{
.new = .{
.vertices = try vertexList.toOwnedSlice(),
.indices = try indexList.toOwnedSlice(),
.jointNames = try jointNames.toOwnedSlice(),
.skeletonName = null,
.name = meshName,
},
};
core.graphics_log("[{s}] vertex count vertices={d} indices={d}", .{ path, rv.new.vertices.len, rv.new.indices.len });
try gMeshPoolBuffer.updateRequests.pushLocked(rv);
}
pub const JointNameEntry = struct {
name: []u8 = undefined,
index: u32 = 0,
pub fn deinit(self: @This()) void {
const allocator = graphics.getContext().allocator;
allocator.free(self.name);
}
};
const objLoader = @import("objLoader");
const vk_allocator = @import("../vk_allocator.zig");
const NeonVkAllocator = vk_allocator.NeonVkAllocator;
const NeonVkBuffer = vk_allocator.NeonVkBuffer;
const mesh = @import("../mesh.zig");
const MeshVertex = mesh.MeshVertex;
const Span = core.Span;
const MergedSpans = core.MergedSpans;
const vk_utils = @import("../vk_utils.zig");
const NeonVkUploader = vk_utils.NeonVkUploader;
const vk_renderer = @import("../vk_renderer.zig");
const NeonVkContext = vk_renderer.NeonVkContext;
const animationSystem = @import("../animation/animationSystem.zig");
const vk_constants = @import("../vk_constants.zig");
const vk_api = @import("../vk_api.zig");
const vkd = vk_api.vkd;
const vki = vk_api.vki;
const vkb = vk_api.vkb;
const graphics = @import("../graphics.zig");

View File

@ -1,71 +0,0 @@
const std = @import("std");
const core = @import("core");
const render_objects = @import("../render_objects.zig");
const Mat = core.Mat;
const Vectorf = core.Vectorf;
const Camera = render_objects.Camera;
pub const NeonVkCameraDataGpu = extern struct {
view: Mat,
proj: Mat,
viewproj: Mat,
viewprojAlt: Mat,
position: Vectorf,
pub fn upload(self: @This(), data: [*]u8) void {
var dataSlice: []u8 = undefined;
dataSlice.ptr = data;
dataSlice.len = @sizeOf(NeonVkCameraDataGpu);
var inputSlice: []const u8 = undefined;
inputSlice.ptr = @as([*]const u8, @ptrCast(&self));
inputSlice.len = @sizeOf(NeonVkCameraDataGpu);
@memcpy(dataSlice, inputSlice);
}
};
// generates NeonVkCameraDataGpu and copies it into the buffer
pub fn memcpyCameraDataToStagedBuffer(camera: *const Camera, data: [*]u8) void {
var cameraData = NeonVkCameraDataGpu{
.proj = camera.projection,
.view = camera.transform,
.viewproj = camera.final,
.viewprojAlt = camera.finalAlt,
.position = camera.position,
};
var dataSlice: []u8 = undefined;
dataSlice.ptr = data;
dataSlice.len = @sizeOf(NeonVkCameraDataGpu);
var inputSlice: []const u8 = undefined;
inputSlice.ptr = @as([*]const u8, @ptrCast(&cameraData));
inputSlice.len = @sizeOf(NeonVkCameraDataGpu);
@memcpy(dataSlice, inputSlice);
}
// upload null to
pub fn uploadNullCameraToBuffer(data: [*]u8) void {
var cameraData = NeonVkCameraDataGpu{
.proj = core.zm.identity(),
.view = core.zm.identity(),
.viewproj = core.zm.identity(),
.viewprojAlt = core.zm.identity(),
.position = .{},
};
var dataSlice: []u8 = undefined;
dataSlice.ptr = data;
dataSlice.len = @sizeOf(NeonVkCameraDataGpu);
var inputSlice: []const u8 = undefined;
inputSlice.ptr = @as([*]const u8, @ptrCast(&cameraData));
inputSlice.len = @sizeOf(NeonVkCameraDataGpu);
@memcpy(dataSlice, inputSlice);
}

View File

@ -1,62 +0,0 @@
const std = @import("std");
const core = @import("core");
const vk = @import("vulkan");
const RenderThread = @import("RenderThread.zig");
// aliases
const Name = core.Name;
const ObjectHandle = core.ObjectHandle;
const MakeTypeName = core.MakeTypeName;
pub const RendererInterfaceRef = core.InterfaceRef(RendererInterface);
// RendererInterfaceVTable
pub const RendererInterface = struct {
typeSize: usize,
typeAlign: usize,
onRendererTeardown: ?*const fn (*anyopaque) void,
sendShared: ?*const fn (*anyopaque, u32) void,
rtPreDraw: ?*const fn (*anyopaque, *RenderThread, vk.CommandBuffer, u32) void,
rtPostDraw: ?*const fn (*anyopaque, *RenderThread, vk.CommandBuffer, u32) void,
pub fn from(comptime TargetType: type) @This() {
const wrappedFuncs = struct {
// === renderthread functions ===
pub fn sendShared(p: *anyopaque, frameIndex: u32) void {
var ptr = @as(*TargetType, @ptrCast(@alignCast(p)));
ptr.sendShared(frameIndex);
}
pub fn rtPreDraw(p: *anyopaque, rt: *RenderThread, cmd: vk.CommandBuffer, frameIndex: u32) void {
var ptr = @as(*TargetType, @ptrCast(@alignCast(p)));
ptr.rtPreDraw(rt, cmd, frameIndex);
}
pub fn rtPostDraw(p: *anyopaque, rt: *RenderThread, cmd: vk.CommandBuffer, frameIndex: u32) void {
var ptr = @as(*TargetType, @ptrCast(@alignCast(p)));
ptr.rtPostDraw(rt, cmd, frameIndex);
}
pub fn onRendererTeardown(pointer: *anyopaque) void {
var ptr = @as(*TargetType, @ptrCast(@alignCast(pointer)));
ptr.onRendererTeardown();
}
};
const self = @This(){
.typeSize = @sizeOf(TargetType),
.typeAlign = @alignOf(TargetType),
.onRendererTeardown = if (@hasDecl(TargetType, "onRendererTeardown")) wrappedFuncs.onRendererTeardown else null,
.sendShared = if (@hasDecl(TargetType, "sendShared")) wrappedFuncs.sendShared else null,
.rtPreDraw = if (@hasDecl(TargetType, "rtPreDraw")) wrappedFuncs.rtPreDraw else null,
.rtPostDraw = if (@hasDecl(TargetType, "rtPostDraw")) wrappedFuncs.rtPostDraw else null,
};
return self;
}
};

View File

@ -1,72 +0,0 @@
const std = @import("std");
const vk = @import("vulkan");
const core = @import("core");
const vk_renderer = @import("../vk_renderer.zig");
const NeonVkContext = vk_renderer.NeonVkContext;
const vk_allocator = @import("../vk_allocator.zig");
const vk_constants = @import("../vk_constants.zig");
pub const NeonVkQueue = struct {
handle: vk.Queue,
family: u32,
pub fn init(vkd: vk_constants.DeviceDispatch, dev: vk.Device, family: u32, index: u32) @This() {
return .{
.handle = vkd.getDeviceQueue(dev, family, index),
.family = family,
};
}
};
pub const NeonVkFrameData = struct {
// descriptors
globalDescriptorSet: vk.DescriptorSet,
objectDescriptorSet: vk.DescriptorSet,
spriteDescriptorSet: vk.DescriptorSet,
// buffers
spriteBuffer: vk_allocator.NeonVkBuffer,
objectBuffer: vk_allocator.NeonVkBuffer,
animationsBuffer: vk_allocator.NeonVkBuffer,
cameraBuffer: vk_allocator.NeonVkBuffer,
};
pub const triangle_mesh_vert = @import("triangle_mesh_vert");
pub const NeonVkObjectDataGpu = triangle_mesh_vert.ObjectData;
pub const VertexBoneData = triangle_mesh_vert.VertexBoneData;
pub const NeonVkSceneDataGpu = struct {
fogColor: core.zm.Vec = .{ 0.0, 0.0, 0.0, 0.0 },
fogDistances: core.zm.Vec = .{ 0.0, 0.0, 0.0, 0.0 },
ambientColor: core.zm.Vec = .{ 0.0, 0.0, 0.0, 0.0 },
sunlightDirection: core.zm.Vec = .{ 0.0, 0.0, 0.0, 0.0 },
sunlightColor: core.zm.Vec = .{ 0.0, 0.0, 0.0, 0.0 },
};
pub const descriptorPoolSizes = [_]vk.DescriptorPoolSize{
.{ .type = .uniform_buffer, .descriptor_count = 1000 },
.{ .type = .uniform_buffer_dynamic, .descriptor_count = 1000 },
.{ .type = .storage_buffer, .descriptor_count = 1000 },
.{ .type = .combined_image_sampler, .descriptor_count = 2000 },
.{ .type = .sampler, .descriptor_count = 1000 },
.{ .type = .sampled_image, .descriptor_count = 1000 },
.{ .type = .storage_image, .descriptor_count = 1000 },
// .{ .type = .sampler, .descriptor_count = 1000 },
// .{ .type = .combined_image_sampler, .descriptor_count = 1000 },
// .{ .type = .sampled_image, .descriptor_count = 1000 },
// .{ .type = .storage_image, .descriptor_count = 1000 },
};
pub const NeonVkSwapImage = struct {
image: vk.Image,
view: vk.ImageView,
imageIndex: usize,
pub fn deinit(self: *NeonVkSwapImage, vkd: vk_constants.DeviceDispatch, dev: vk.Device) void {
vkd.destroyImageView(dev, self.view, null);
}
};

View File

@ -1,44 +0,0 @@
pub const SkeletalBuffers = struct {
descriptorSet: vk.DescriptorSet,
gc: *NeonVkContext,
vkAllocator: *NeonVkAllocator,
allocator: std.mem.Allocator,
finalsBuffer: [2]NeonVkBuffer = undefined,
pub fn init(gc: *NeonVkContext) !*@This() {
const self = try gc.allocator.create(@This());
self.* = .{
.gc = gc,
.allocator = gc.allocator,
.vkAllocator = gc.vkAllocator,
.skeletalPipeData = undefined,
};
self.buildBuffers();
return self;
}
pub fn buildBuffers(self: *@This()) !void {
const vkAllocator: *NeonVkAllocator = self.vkAllocator;
// 100k animated skeletal mesh vertices ought to be enough for anyone right?
for (0..2) |i| {
self.finalsBuffer[i] = try vkAllocator.createSsboBuffer(@sizeOf(core.Mat) * vk_constants.MAX_SKIN_SLOTS, "bones buffer.");
}
}
};
const std = @import("std");
const core = @import("core");
const assets = @import("assets");
const graphics = @import("../graphics.zig");
const vk_renderer_types = @import("vk_renderer_types.zig");
const VertexBoneData = vk_renderer_types.VertexBoneData;
const gpd = graphics.gpu_pipe_data;
const NeonVkContext = graphics.NeonVkContext;
const NeonVkBuffer = graphics.NeonVkBuffer;
const NeonVkAllocator = graphics.NeonVkAllocator;
const vk = @import("vulkan");
const vk_constants = @import("../vk_constants.zig");

View File

@ -1,87 +0,0 @@
const std = @import("std");
const vk = @import("vulkan");
const core = @import("core");
const vk_constants = @import("../vk_constants.zig");
const vk_renderer_types = @import("vk_renderer_types.zig");
const vk_api = @import("../vk_api.zig");
const vkd = vk_api.vkd;
const vki = vk_api.vki;
const vkb = vk_api.vkb;
const force_mailbox = core.BuildOption("force_mailbox");
pub fn findSurfaceFormat(
allocator: std.mem.Allocator,
pdev: vk.PhysicalDevice,
surface: vk.SurfaceKHR,
) !vk.SurfaceFormatKHR {
const preferred = vk.SurfaceFormatKHR{
.format = .b8g8r8a8_srgb,
.color_space = .srgb_nonlinear_khr,
};
var count: u32 = 0;
_ = try vki.getPhysicalDeviceSurfaceFormatsKHR(pdev, surface, &count, null);
const surface_formats = try allocator.alloc(vk.SurfaceFormatKHR, count);
defer allocator.free(surface_formats);
_ = try vki.getPhysicalDeviceSurfaceFormatsKHR(pdev, surface, &count, surface_formats.ptr);
for (surface_formats) |sfmt| {
if (std.meta.eql(sfmt, preferred)) {
return preferred;
}
}
const rv = surface_formats[0];
core.graphics_log("Selected surface format\n {any}", .{rv});
return rv;
}
pub fn findPresentMode(
allocator: std.mem.Allocator,
pdev: vk.PhysicalDevice,
surface: vk.SurfaceKHR,
) !vk.PresentModeKHR {
var count: u32 = undefined;
_ = try vki.getPhysicalDeviceSurfacePresentModesKHR(pdev, surface, &count, null);
const present_modes = try allocator.alloc(vk.PresentModeKHR, count);
defer allocator.free(present_modes);
_ = try vki.getPhysicalDeviceSurfacePresentModesKHR(pdev, surface, &count, present_modes.ptr);
const preferred = [_]vk.PresentModeKHR{
.fifo_khr,
.mailbox_khr,
.immediate_khr,
};
if (force_mailbox) {
return .mailbox_khr;
}
for (preferred) |mode| {
if (std.mem.indexOfScalar(vk.PresentModeKHR, present_modes, mode) != null) {
return mode;
}
}
return error.UnableToFindPresentMode;
}
pub fn findActualExtent(
extent: vk.Extent2D,
caps: vk.SurfaceCapabilitiesKHR,
) !vk.Extent2D {
if (caps.current_extent.width != 0xFFFF_FFFF) {
return caps.current_extent;
} else {
return .{
.width = std.math.clamp(extent.width, caps.min_image_extent.width, caps.max_image_extent.width),
.height = std.math.clamp(extent.height, caps.min_image_extent.height, caps.max_image_extent.height),
};
}
}

View File

@ -1,128 +0,0 @@
// this implements the global texture list
const gTextureList: *TextureList = undefined;
pub const ArrayedTexture = struct {};
pub const TextureList = struct {
allocator: std.mem.Allocator,
textures: std.AutoHashMapUnmanaged(u32, *Texture),
gc: *NeonVkContext,
listSet: vk.DescriptorSet = undefined,
dsl: vk.DescriptorSetLayout = undefined,
// descriptorPool: vk.DescriptorPool = undefined,
// const descriptorPoolSizes = [_]vk.DescriptorPoolSize{
// .{ .type = .sampler, .descriptor_count = 1000 },
// .{ .type = .combined_image_sampler, .descriptor_count = 1000 },
// .{ .type = .sampled_image, .descriptor_count = 1000 },
// .{ .type = .storage_image, .descriptor_count = 1000 },
// };
pub fn create(gc: *NeonVkContext) !*@This() {
const self = try gc.allocator.create(@This());
self.* = .{
.allocator = gc.allocator,
.textures = .{},
.gc = gc,
};
try self.initTextureList();
return self;
}
pub fn initTextureList(self: *@This()) !void {
// var poolInfo = vk.DescriptorPoolCreateInfo{
// .flags = .{},
// .max_sets = 1000,
// .pool_size_count = @intCast(descriptorPoolSizes.len),
// .p_pool_sizes = &descriptorPoolSizes,
// };
// self.descriptorPool = try vkd.createDescriptorPool(self.gc.dev, &poolInfo, null);
const bindings = [_]vk.DescriptorSetLayoutBinding{
.{
.binding = 0,
.descriptor_type = .storage_buffer,
.descriptor_count = 500,
.stage_flags = .{
.vertex_bit = true,
.geometry_bit = true,
.compute_bit = true,
.fragment_bit = true,
},
.p_immutable_samplers = null,
},
.{
.binding = 1,
.descriptor_type = .combined_image_sampler,
.descriptor_count = 500,
.stage_flags = .{
.vertex_bit = true,
.geometry_bit = true,
.compute_bit = true,
.fragment_bit = true,
},
.p_immutable_samplers = null,
},
.{
.binding = 2,
.descriptor_type = .storage_image,
.descriptor_count = 500,
.stage_flags = .{
.vertex_bit = true,
.geometry_bit = true,
.compute_bit = true,
.fragment_bit = true,
},
.p_immutable_samplers = null,
},
};
const flags = [_]vk.DescriptorBindingFlags{
.{ .partially_bound_bit = true },
.{ .partially_bound_bit = true },
.{ .partially_bound_bit = true },
};
const fci = vk.DescriptorSetLayoutBindingFlagsCreateInfo{ .binding_count = 3, .p_binding_flags = @ptrCast(&flags) };
const dsci = vk.DescriptorSetLayoutCreateInfo{
.flags = .{},
.binding_count = bindings.len,
.p_bindings = @ptrCast(&bindings),
.p_next = &fci,
};
self.dsl = try vkd.createDescriptorSetLayout(self.gc.dev, &dsci, null);
const dsai = vk.DescriptorSetAllocateInfo{
.descriptor_pool = self.gc.descriptorPool,
.descriptor_set_count = 1,
.p_set_layouts = @ptrCast(&self.dsl),
};
try vkd.allocateDescriptorSets(self.gc.dev, &dsai, @ptrCast(&self.listSet));
}
pub fn destroy(self: *@This()) void {
vkd.destroyDescriptorSetLayout(self.gc.dev, self.dsl, null);
// vkd.destroyDescriptorPool(self.gc.dev, self.descriptorPool, null);
self.textures.deinit(self.allocator);
self.allocator.destroy(self);
}
};
const graphics = @import("../graphics.zig");
const NeonVkContext = graphics.NeonVkContext;
const texture = @import("../texture.zig");
const Texture = texture.Texture;
const vk = @import("vulkan");
const std = @import("std");
const vk_api = @import("../vk_api.zig");
const vkd = vk_api.vkd;

View File

@ -1,35 +0,0 @@
const vk_constants = @import("../vk_constants.zig");
const vk_api = @import("../vk_api.zig");
const vkd = vk_api.vkd;
const vki = vk_api.vki;
const vkb = vk_api.vkb;
const vk = @import("vulkan");
const graphics = @import("../graphics.zig");
const NeonVkBuffer = graphics.NeonVkBuffer;
pub fn copyStagingSlice(
comptime Element: type,
cmd: vk.CommandBuffer,
params: struct {
src: *NeonVkBuffer,
dst: *NeonVkBuffer,
size: u32, // in element count
src_offset: u32 = 0, // in element counts
dst_offset: u32 = 0, // in element counts
},
) void {
const elementSize = @sizeOf(Element);
var copy = vk.BufferCopy{
.dst_offset = params.dst_offset * elementSize,
.src_offset = params.src_offset * elementSize,
.size = params.size * elementSize,
};
vkd.cmdCopyBuffer(
cmd,
params.src.buffer,
params.dst.buffer,
1,
@as([*]const vk.BufferCopy, @ptrCast(&copy)),
);
}

View File

@ -1,11 +0,0 @@
const std = @import("std");
const core = @import("core");
const graphics = @import("graphics.zig");
const PixelBufferRGA8 = @import("PixelBufferRGBA8.zig");
pub fn updateTextureFromPixelsSync(
textureName: core.Name,
pixelBuffer: PixelBufferRGA8,
) void {
graphics.getContext().updateTextureFromPixelsSync(textureName, pixelBuffer);
}

View File

@ -1 +0,0 @@
// vk_virtual

View File

@ -1,95 +0,0 @@
// REEEEEEEEEEEEEE
const std = @import("std");
const core = @import("core");
const vk = @import("vulkan");
const vma = @import("vma");
const vk_constants = @import("vk_constants.zig");
const vk_api = @import("vk_api.zig");
const vkd = vk_api.vkd;
pub fn transferDst_into_shaderReadOnly(
cmd: vk.CommandBuffer,
image: vk.Image,
mipLevel: u32,
baseArrayLayer: u32,
layerCount: u32,
) void {
if (mipLevel == 0) {
core.engine_logs("mipLevel 0 detected into_shaderReadOnly");
}
const range = vk.ImageSubresourceRange{
.aspect_mask = .{ .color_bit = true },
.base_mip_level = 0,
.level_count = mipLevel,
.base_array_layer = baseArrayLayer,
.layer_count = layerCount,
};
var imageBarrier_toReadable = vk.ImageMemoryBarrier{
.old_layout = .undefined,
.new_layout = .shader_read_only_optimal,
.image = image,
.subresource_range = range,
.src_access_mask = .{ .transfer_write_bit = true },
.dst_access_mask = .{ .shader_read_bit = false },
.src_queue_family_index = 0,
.dst_queue_family_index = 0,
};
vkd.cmdPipelineBarrier(
cmd,
.{ .transfer_bit = true },
.{ .fragment_shader_bit = true },
.{},
0,
undefined,
0,
undefined,
1,
@ptrCast(&imageBarrier_toReadable),
);
}
pub fn into_transferDst(
cmd: vk.CommandBuffer,
image: vk.Image,
mipLevel: u32,
baseArrayLayer: u32,
layerCount: u32,
) void {
if (mipLevel == 0) {
core.engine_logs("mipLevel 0 detected into_transferDst");
}
const range = vk.ImageSubresourceRange{
.aspect_mask = .{ .color_bit = true },
.base_mip_level = 0,
.level_count = mipLevel,
.base_array_layer = baseArrayLayer,
.layer_count = layerCount,
};
var imageBarrier_toTransfer = vk.ImageMemoryBarrier{
.old_layout = .undefined,
.new_layout = .transfer_dst_optimal,
.image = image,
.subresource_range = range,
.src_access_mask = .{},
.dst_access_mask = .{ .transfer_write_bit = true },
.src_queue_family_index = 0,
.dst_queue_family_index = 0,
};
vkd.cmdPipelineBarrier(
cmd,
.{ .top_of_pipe_bit = true },
.{ .transfer_bit = true },
.{},
0,
undefined,
0,
undefined,
1,
@ptrCast(&imageBarrier_toTransfer),
);
}

View File

@ -1,550 +0,0 @@
const std = @import("std");
const core = @import("core");
const vk_renderer = @import("vk_renderer.zig");
const vma = @import("vma");
const graphics = @import("graphics.zig");
const vk = @import("vulkan");
const vkinit = @import("vk_init.zig");
const vk_constants = @import("vk_constants.zig");
const tracy = core.tracy;
const Texture = @import("texture.zig").Texture;
const memory = core.MemoryTracker;
const vk_allocator = @import("vk_allocator.zig");
const vk_api = @import("vk_api.zig");
const vkd = vk_api.vkd;
const NeonVkAllocator = vk_allocator.NeonVkAllocator;
const png = core.png;
const PngContents = png.PngContents;
const ArrayList = std.ArrayList;
const Vectorf = core.Vectorf;
const NeonVkContext = vk_renderer.NeonVkContext;
const NeonVkBuffer = vk_renderer.NeonVkBuffer;
const NeonVkImage = vk_renderer.NeonVkImage;
const NumFrames = vk_constants.NUM_FRAMES;
const NeonVkObjectDataGpu = vk_renderer.NeonVkObjectDataGpu;
const transitions = @import("vk_transitions.zig");
const NeonVkSpriteDataGpu = struct {
// tl, tr, br, bl running clockwise
position: core.zm.Vec = .{ 0.0, 0.0, 0.0, 0.0 },
size: core.Vector2f = .{ .x = 1.0, .y = 1.0 },
};
// Takes the contents of a png file and transfers the pixel contents to a staged buffer
pub fn stagePixels(self: PngContents, ctx: *NeonVkContext) !NeonVkBuffer {
const stagingBuffer = try ctx.create_buffer(self.pixels.len, .{ .transfer_src_bit = true }, .cpuOnly, "Stage pixels staging buffer");
const data = try ctx.vkAllocator.vmaAllocator.mapMemory(stagingBuffer.allocation, u8);
var dataSlice: []u8 = undefined;
dataSlice.ptr = data;
dataSlice.len = self.pixels.len;
@memcpy(dataSlice, self.pixels);
ctx.vkAllocator.vmaAllocator.unmapMemory(stagingBuffer.allocation);
return stagingBuffer;
}
pub const LoadAndStageImage = struct {
stagingBuffer: NeonVkBuffer,
image: NeonVkImage,
mipLevel: u32 = 0,
cubeOffsets: ?[6]u32 = null,
pub fn deinit(self: *@This(), vkAllocator: *NeonVkAllocator) void {
vkAllocator.destroyBuffer(&self.stagingBuffer);
vkAllocator.destroyImage(&self.image);
}
};
pub fn stagePixelsRaw(pixels: []const u8, ctx: *NeonVkContext) !NeonVkBuffer {
const stagingBuffer = try ctx.create_buffer(pixels.len, .{ .transfer_src_bit = true }, .cpuOnly, "Stage pixels staging buffer");
const data = try ctx.vkAllocator.vmaAllocator.mapMemory(stagingBuffer.allocation, u8);
var dataSlice: []u8 = undefined;
dataSlice.ptr = data;
dataSlice.len = pixels.len;
@memcpy(dataSlice, pixels);
ctx.vkAllocator.vmaAllocator.unmapMemory(stagingBuffer.allocation);
return stagingBuffer;
}
pub fn newVkImage(size: core.Vector2i, ctx: *NeonVkContext, mipLevel: u32) !NeonVkImage {
const imageExtent = vk.Extent3D{
.width = @as(u32, @intCast(size.x)),
.height = @as(u32, @intCast(size.y)),
.depth = 1,
};
var imgCreateInfo = vkinit.imageCreateInfo(.r8g8b8a8_srgb, .{
.sampled_bit = true,
.transfer_dst_bit = true,
}, imageExtent, mipLevel);
if (mipLevel > 1) {
// core.graphics_log("creating image with mip level: {d} {d}x{d}", .{ mipLevel, size.x, size.y });
imgCreateInfo.usage = .{
.transfer_dst_bit = true,
.transfer_src_bit = true,
.sampled_bit = true,
};
}
const imgAllocInfo = vma.AllocationCreateInfo{
.requiredFlags = .{},
.usage = .gpuOnly,
};
return try ctx.vkAllocator.createImage(imgCreateInfo, imgAllocInfo, @src().fn_name);
}
inline fn isPowerOfTwo(n: anytype) bool {
return n != 0 and (n & (n - 1)) == 0;
}
pub fn getMiplevelFromSize(size: core.Vector2i) u32 {
if (size.x != size.y)
return 1;
if (!isPowerOfTwo(size.x))
return 1;
return std.math.log2(@as(u32, @intCast(@max(size.x, size.y)))) + 1;
}
pub fn createTextureFromPixels(
pixels: []const u8,
size: core.Vector2i,
ctx: *NeonVkContext,
useBlocky: bool,
) !CreateTextureResults {
// copy pixels into staging buffer
const miplevel = getMiplevelFromSize(size);
var stagingBuffer = try stagePixelsRaw(pixels, ctx);
// create image memory resources
const createdImage = try newVkImage(size, ctx, miplevel);
// upload staging buffer
try submit_copy_from_staging(ctx, stagingBuffer, createdImage, miplevel);
stagingBuffer.deinit(ctx.vkAllocator);
var imageViewCreate = vkinit.imageViewCreateInfo(
.r8g8b8a8_srgb,
createdImage.image,
.{ .color_bit = true },
miplevel,
);
const imageView = try ctx.vkd.createImageView(ctx.dev, &imageViewCreate, null);
const newTexture = try ctx.allocator.create(Texture);
newTexture.* = Texture{
.image = createdImage,
.imageView = imageView,
};
// create descriptors for
const rv = ctx.create_mesh_image_for_texture(newTexture.*, .{
.useBlocky = useBlocky,
}) catch unreachable;
return .{ .texture = newTexture, .descriptor = rv.textureSet, .textureId = rv.textureId };
}
const CreateTextureResults = struct { texture: *Texture, descriptor: vk.DescriptorSet, textureId: u32 };
pub fn createAndInstallTextureFromPixels(
textureName: core.Name,
pixels: []const u8,
size: core.Vector2i,
ctx: *NeonVkContext,
useBlocky: bool,
) !CreateTextureResults {
const res = try createTextureFromPixels(pixels, size, ctx, useBlocky);
ctx.install_texture_into_registry(textureName, res.texture, res.descriptor, res.textureId) catch return error.UnknownStatePanic;
return res;
}
pub fn load_and_stage_image_from_bytes(ctx: *NeonVkContext, bytes: []const u8) !LoadAndStageImage {
var pngContents = try PngContents.initFromBytes(ctx.allocator, "embeddedFile", bytes);
defer pngContents.deinit();
return try load_and_stage_image(ctx, pngContents);
}
pub fn load_and_stage_image_from_file(ctx: *NeonVkContext, filePath: []const u8) !LoadAndStageImage {
// When you record command buffers, their command pools can only be used from
// one thread at a time. While you can create multiple command buffers from a
// command pool, you cant fill those commands from multiple threads. If you
// want to record command buffers from multiple threads, then you will need
// more command pools, one per thread.
//
// in other words... this will multithread our png loading... yes.
//
// and we can multithread constructing our command structures
// but calling VkQueueSubmit is not going to be threadsafe unless we create a
// seperate command pool for each thread.
// 1. check if there is a cooked one.
var pngContents: PngContents = undefined;
const allocator = ctx.allocator;
var cookedPath = std.ArrayList(u8).init(allocator);
defer cookedPath.deinit();
try cookedPath.appendSlice("_cooked/");
try cookedPath.appendSlice(filePath);
try cookedPath.appendSlice(".Texture");
if (core.fs().fileExists(cookedPath.items)) {
pngContents = try PngContents.initFromFSCooked(core.fs(), ctx.allocator, cookedPath.items);
} else {
pngContents = try PngContents.initFromFS(core.fs(), ctx.allocator, filePath);
}
defer pngContents.deinit();
return try load_and_stage_image(ctx, pngContents);
}
pub fn load_and_stage_image(ctx: *NeonVkContext, pngContents: PngContents) !LoadAndStageImage {
const imageExtent = vk.Extent3D{
.width = @as(u32, @intCast(pngContents.size.x)),
.height = @as(u32, @intCast(pngContents.size.y)),
.depth = 1,
};
const mipLevel = std.math.log2(@max(imageExtent.width, imageExtent.height)) + 1;
var imgCreateInfo = vkinit.imageCreateInfo(.r8g8b8a8_srgb, .{
.sampled_bit = true,
.transfer_dst_bit = true,
}, imageExtent, mipLevel);
if (mipLevel > 1) {
imgCreateInfo.usage.transfer_src_bit = true;
}
const imgAllocInfo = vma.AllocationCreateInfo{
.requiredFlags = .{},
.usage = .gpuOnly,
};
const newImage = try ctx.vkAllocator.createImage(imgCreateInfo, imgAllocInfo, "saved image vk_renderer.Texture");
const stagingBuffer = try stagePixels(pngContents, ctx);
return .{
.stagingBuffer = stagingBuffer,
.image = newImage,
.mipLevel = mipLevel,
};
}
pub fn submit_copy_from_staging(ctx: *NeonVkContext, stagingBuffer: NeonVkBuffer, newImage: NeonVkImage, mipLevel: u32) !void {
var z1 = tracy.ZoneN(@src(), "submitting copy from staging buffer");
defer z1.End();
try ctx.uploader.startUploadContext();
{
var z2 = tracy.ZoneN(@src(), "recording command buffer");
const cmd = ctx.uploader.commandBuffer;
transitions.into_transferDst(cmd, newImage.image, mipLevel, 0, 1);
var copyRegion = vk.BufferImageCopy{
.buffer_offset = 0,
.buffer_row_length = 0,
.buffer_image_height = 0,
.image_offset = std.mem.zeroes(vk.Offset3D),
.image_subresource = .{
.aspect_mask = .{ .color_bit = true },
.mip_level = 0,
.base_array_layer = 0,
.layer_count = 1,
},
.image_extent = .{
.width = newImage.pixelWidth,
.height = newImage.pixelHeight,
.depth = 1,
},
};
vkd.cmdCopyBufferToImage(
cmd,
stagingBuffer.buffer,
newImage.image,
.transfer_dst_optimal,
1,
@ptrCast(&copyRegion),
);
// core.graphics_log("miplevel count: {d}", .{mipLevel});
try generateMipMaps(cmd, newImage, mipLevel, 0);
transitions.transferDst_into_shaderReadOnly(cmd, newImage.image, mipLevel, 0, 1);
z2.End();
}
try ctx.uploader.finishUploadContext();
//try ctx.finish_upload_context(&ctx.uploadContext);
}
pub fn generateMipMaps(cmd: vk.CommandBuffer, vkImage: NeonVkImage, mipLevels: u32, baseArrayLayer: u32) !void {
try core.assert(mipLevels > 0);
const img = vkImage.image;
const range: vk.ImageSubresourceRange = .{
.aspect_mask = .{ .color_bit = true },
.base_mip_level = 0,
.level_count = 1,
.base_array_layer = baseArrayLayer,
.layer_count = 1,
};
var imb: vk.ImageMemoryBarrier = .{
.old_layout = .undefined,
.new_layout = .transfer_dst_optimal,
.image = img,
.src_access_mask = .{},
.dst_access_mask = .{},
.subresource_range = range,
.src_queue_family_index = 0, // 0 == ignored
.dst_queue_family_index = 0,
};
var width = @as(i32, @intCast(vkImage.pixelWidth));
var height = @as(i32, @intCast(vkImage.pixelHeight));
for (1..mipLevels) |i| {
imb.subresource_range.base_mip_level = @as(u32, @intCast(i)) - 1;
imb.old_layout = .undefined;
imb.new_layout = .transfer_src_optimal;
imb.src_access_mask = .{
.transfer_write_bit = true,
};
imb.dst_access_mask = .{
.transfer_read_bit = true,
};
vkd.cmdPipelineBarrier(cmd, .{
.transfer_bit = true,
}, .{
.transfer_bit = true,
}, .{}, 0, undefined, 0, undefined, 1, @ptrCast(&imb));
var blit: vk.ImageBlit = undefined;
blit.src_offsets[0] = .{ .x = 0, .y = 0, .z = 0 };
blit.src_offsets[1] = .{ .x = width, .y = height, .z = 1 };
blit.src_subresource = .{
.aspect_mask = .{ .color_bit = true },
.mip_level = @as(u32, @intCast(i)) - 1,
.base_array_layer = baseArrayLayer,
.layer_count = 1,
};
var dstWidth: i32 = 1;
if (width > 1) {
dstWidth = @divFloor(width, 2);
}
var dstHeight: i32 = 1;
if (height > 1) {
dstHeight = @divFloor(height, 2);
}
blit.dst_offsets[0] = .{ .x = 0, .y = 0, .z = 0 };
blit.dst_offsets[1] = .{ .x = dstWidth, .y = dstHeight, .z = 1 };
blit.dst_subresource = .{
.aspect_mask = .{ .color_bit = true },
.mip_level = @as(u32, @intCast(i)),
.base_array_layer = 0,
.layer_count = 1,
};
vkd.cmdBlitImage(
cmd,
img,
.transfer_src_optimal,
img,
.transfer_dst_optimal,
1,
@ptrCast(&blit),
.linear,
);
width = dstWidth;
height = dstHeight;
}
}
// A better encapsulated version of the NeonVkUploadContext
pub const NeonVkUploader = struct {
gc: *NeonVkContext,
arena: std.heap.ArenaAllocator,
allocator: std.mem.Allocator,
uploadFence: vk.Fence = undefined,
commandPool: vk.CommandPool = undefined,
commandBuffer: vk.CommandBuffer = undefined,
mutex: std.Thread.Mutex = .{},
isActive: bool = false,
tag: []const u8,
pub fn init(gc: *NeonVkContext, comptime tag: []const u8) !@This() {
var arena = std.heap.ArenaAllocator.init(gc.allocator);
var self = @This(){
.arena = arena,
.allocator = arena.allocator(),
.gc = gc,
.tag = tag,
};
// create the uploadFence
var fci = vk.FenceCreateInfo{
.flags = .{ .signaled_bit = false },
};
self.uploadFence = try vkd.createFence(self.gc.dev, &fci, null);
// create the command pool
var cpci = vkinit.commandPoolCreateInfo(@as(u32, @intCast(self.gc.graphicsFamilyIndex)), .{ .reset_command_buffer_bit = true });
self.commandPool = try vkd.createCommandPool(self.gc.dev, &cpci, null);
// create the command buffer
var cbai = vk.CommandBufferAllocateInfo{
.command_pool = self.commandPool,
.level = vk.CommandBufferLevel.primary,
.command_buffer_count = 1,
};
try vkd.allocateCommandBuffers(
self.gc.dev,
&cbai,
@as([*]vk.CommandBuffer, @ptrCast(&self.commandBuffer)),
);
return self;
}
pub fn startUploadContext(self: *@This()) !void {
self.mutex.lock();
var cbi = vkinit.commandBufferBeginInfo(.{ .one_time_submit_bit = true });
try vkd.beginCommandBuffer(self.commandBuffer, &cbi);
self.isActive = true;
}
pub fn addBufferUpload(
self: *@This(),
stagingBuffer: NeonVkBuffer,
targetBuffer: NeonVkBuffer,
transferSize: u32,
) !void {
core.assert(self.isActive);
var copy = vk.BufferCopy{
.dst_offset = 0,
.src_offset = 0,
.size = transferSize,
};
const cmd = self.commandBuffer;
vkd.cmdCopyBuffer(
cmd,
stagingBuffer.buffer,
targetBuffer.buffer,
1,
@as([*]const vk.BufferCopy, @ptrCast(&copy)),
);
}
pub fn waitForFences(self: *@This()) !void {
_ = try vkd.waitForFences(
self.gc.dev,
1,
@as([*]const vk.Fence, @ptrCast(&self.uploadFence)),
1,
1000000000,
);
try vkd.resetFences(self.gc.dev, 1, @as([*]const vk.Fence, @ptrCast(&self.uploadFence)));
self.isActive = false;
self.mutex.unlock();
}
pub fn submitUploads(self: *@This()) !void {
try vkd.endCommandBuffer(self.commandBuffer);
var submit = vkinit.submitInfo(&self.commandBuffer);
// !!!!!!!!!!!!!!!!!!!!!!!!!!
// there should be a dedicated uploader queue.
// .. but thats not something that is always going to be available.
// !!!!!!!!!!!!!!!!!!!!!!!!!!
try vkd.queueSubmit(
self.gc.graphicsQueue.handle,
1,
@as([*]const vk.SubmitInfo, @ptrCast(&submit)),
self.uploadFence,
);
}
pub fn finishUploadContext(self: *@This()) !void {
try self.submitUploads();
try self.waitForFences();
}
pub fn deinit(self: *@This()) void {
vkd.destroyCommandPool(self.gc.dev, self.commandPool, null);
vkd.destroyFence(self.gc.dev, self.uploadFence, null);
}
};
pub const NeonVkUploadContext = struct {
uploadFence: vk.Fence,
commandPool: vk.CommandPool,
commandBuffer: vk.CommandBuffer,
mutex: std.Thread.Mutex = .{},
active: bool = false,
};
pub fn createDescriptorSetForImage(
dev: vk.Device,
descriptorPool: vk.DescriptorPool,
layout: vk.DescriptorSetLayout,
imageView: vk.ImageView,
sampler: vk.Sampler,
addToGlobal: bool,
) !struct { textureSet: vk.DescriptorSet, textureId: u32 } {
// var textureSet = try self.allocator.create(vk.DescriptorSet);
var textureSet: vk.DescriptorSet = undefined;
var allocInfo = vk.DescriptorSetAllocateInfo{
.descriptor_pool = descriptorPool,
.descriptor_set_count = 1,
.p_set_layouts = @ptrCast(&layout),
};
try vkd.allocateDescriptorSets(dev, &allocInfo, @as([*]vk.DescriptorSet, @ptrCast(&textureSet)));
var imageBufferInfo = vk.DescriptorImageInfo{
.sampler = sampler,
.image_view = imageView,
.image_layout = .shader_read_only_optimal,
};
var writeDescriptorSet = vkinit.writeDescriptorImage(
.combined_image_sampler,
textureSet,
&imageBufferInfo,
0,
);
vkd.updateDescriptorSets(dev, 1, @ptrCast(&writeDescriptorSet), 0, undefined);
const gc = graphics.getContext();
const newTextureId = gc.newTextureId;
// ruh roh, that's a big todo to do in the future
if (addToGlobal) {
gc.newTextureId += 1;
try gc.newMeshImages.pushLocked(.{ .bufferInfo = imageBufferInfo, .textureId = newTextureId });
}
return .{ .textureSet = textureSet, .textureId = if (addToGlobal) newTextureId else 0 };
}

View File

@ -1,143 +0,0 @@
// Game: Deathwish
// Format: Standard
// entity 0
{
"classname" "worldspawn"
// brush 0
{
( -224 -32 -16 ) ( -224 -31 -16 ) ( -224 -32 -15 ) ProtoFloor 0 -16 0 1 1
( -224 -32 -16 ) ( -224 -32 -15 ) ( -223 -32 -16 ) ProtoFloor 0 -16 0 1 1
( -224 -32 -16 ) ( -223 -32 -16 ) ( -224 -31 -16 ) ProtoFloor 0 0 0 1 1
( -192 32 0 ) ( -192 33 0 ) ( -191 32 0 ) ProtoFloor 0 0 0 1 1
( -192 32 0 ) ( -191 32 0 ) ( -192 32 1 ) ProtoFloor 0 -16 0 1 1
( -64 32 0 ) ( -64 32 1 ) ( -64 33 0 ) ProtoFloor 0 -16 0 1 1
}
// brush 1
{
( -576 -160 -32 ) ( -576 -159 -32 ) ( -576 -160 -31 ) ProtoGrass 16 -16 0 1 1
( 0 -400 -32 ) ( 0 -400 -31 ) ( 1 -400 -32 ) ProtoGrass -16 -16 0 1 1
( 0 -160 -32 ) ( 1 -160 -32 ) ( 0 -159 -32 ) ProtoGrass -16 -16 0 1 1
( 48 -144 -16 ) ( 48 -143 -16 ) ( 49 -144 -16 ) ProtoGrass -16 -16 0 1 1
( 48 224 -16 ) ( 49 224 -16 ) ( 48 224 -15 ) ProtoGrass -16 -16 0 1 1
( 224 -144 -16 ) ( 224 -144 -15 ) ( 224 -143 -16 ) ProtoGrass 16 -16 0 1 1
}
// brush 2
{
( -48 -144 -32 ) ( -48 -143 -32 ) ( -48 -144 -31 ) ProtoFloor 0 -32 0 1 1
( -48 -144 -32 ) ( -48 -144 -31 ) ( -47 -144 -32 ) ProtoFloor 0 -32 0 1 1
( -48 -144 -32 ) ( -47 -144 -32 ) ( -48 -143 -32 ) ProtoFloor 0 0 0 1 1
( 80 -64 0 ) ( 80 -63 0 ) ( 81 -64 0 ) ProtoFloor 0 0 0 1 1
( 80 -64 -16 ) ( 81 -64 -16 ) ( 80 -64 -15 ) ProtoFloor 0 -32 0 1 1
( 80 -64 -16 ) ( 80 -64 -15 ) ( 80 -63 -16 ) ProtoFloor 0 -32 0 1 1
}
// brush 3
{
( -48 -48 -32 ) ( -48 -47 -32 ) ( -48 -48 -31 ) ProtoFloor 32 -32 0 1 1
( -48 -48 -32 ) ( -48 -48 -31 ) ( -47 -48 -32 ) ProtoFloor 0 -32 0 1 1
( -48 -48 -32 ) ( -47 -48 -32 ) ( -48 -47 -32 ) ProtoFloor 0 -32 0 1 1
( 80 32 16 ) ( 80 33 16 ) ( 81 32 16 ) ProtoFloor 0 -32 0 1 1
( 80 32 -16 ) ( 81 32 -16 ) ( 80 32 -15 ) ProtoFloor 0 -32 0 1 1
( 80 32 -16 ) ( 80 32 -15 ) ( 80 33 -16 ) ProtoFloor 32 -32 0 1 1
}
// brush 4
{
( -448 -96 -16 ) ( -448 -95 -16 ) ( -448 -96 -15 ) ProtoWallsGrey 0 0 0 1 1
( -448 -96 -16 ) ( -448 -96 -15 ) ( -447 -96 -16 ) ProtoWallsGrey 0 0 0 1 1
( -448 -96 -16 ) ( -447 -96 -16 ) ( -448 -95 -16 ) ProtoWallsGrey 0 0 0 1 1
( -432 80 48 ) ( -432 81 48 ) ( -431 80 48 ) ProtoWallsGrey 0 0 0 1 1
( -432 80 0 ) ( -431 80 0 ) ( -432 80 1 ) ProtoWallsGrey 0 0 0 1 1
( -432 80 0 ) ( -432 80 1 ) ( -432 81 0 ) ProtoWallsGrey 0 0 0 1 1
}
// brush 5
{
( -416 -256 -16 ) ( -416 -255 -16 ) ( -416 -256 -15 ) ProtoWallsOrange 0 0 0 1 1
( -496 -256 -16 ) ( -496 -256 -15 ) ( -495 -256 -16 ) ProtoWallsOrange 0 0 0 1 1
( -496 -256 -16 ) ( -495 -256 -16 ) ( -496 -255 -16 ) ProtoWallsOrange 0 0 0 1 1
( -304 -240 96 ) ( -304 -239 96 ) ( -303 -240 96 ) ProtoWallsOrange 0 0 0 1 1
( -304 -240 0 ) ( -303 -240 0 ) ( -304 -240 1 ) ProtoWallsOrange 0 0 0 1 1
( -304 -240 0 ) ( -304 -240 1 ) ( -304 -239 0 ) ProtoWallsOrange 0 0 0 1 1
}
// brush 6
{
( -320 -384 -16 ) ( -320 -383 -16 ) ( -320 -384 -15 ) ProtoWallsOrange 0 0 0 1 1
( -320 -320 -16 ) ( -320 -320 -15 ) ( -319 -320 -16 ) ProtoWallsOrange 0 0 0 1 1
( -320 -384 -16 ) ( -319 -384 -16 ) ( -320 -383 -16 ) ProtoWallsOrange 0 0 0 1 1
( -304 -256 96 ) ( -304 -255 96 ) ( -303 -256 96 ) ProtoWallsOrange 0 0 0 1 1
( -304 -256 0 ) ( -303 -256 0 ) ( -304 -256 1 ) ProtoWallsOrange 0 0 0 1 1
( -304 -256 0 ) ( -304 -256 1 ) ( -304 -255 0 ) ProtoWallsOrange 0 0 0 1 1
}
// brush 7
{
( -480 -256 80 ) ( -480 -255 80 ) ( -480 -256 81 ) ProtoWallsOrange 0 0 0 1 1
( -432 -256 80 ) ( -432 -256 81 ) ( -431 -256 80 ) ProtoWallsOrange 0 0 0 1 1
( -432 -256 64 ) ( -431 -256 64 ) ( -432 -255 64 ) ProtoWallsOrange 0 0 0 1 1
( -416 -240 96 ) ( -416 -239 96 ) ( -415 -240 96 ) ProtoWallsOrange 0 0 0 1 1
( -416 -240 96 ) ( -415 -240 96 ) ( -416 -240 97 ) ProtoWallsOrange 0 0 0 1 1
( -416 -240 96 ) ( -416 -240 97 ) ( -416 -239 96 ) ProtoWallsOrange 0 0 0 1 1
}
// brush 8
{
( -576 -256 -16 ) ( -576 -255 -16 ) ( -576 -256 -15 ) ProtoWallsOrange 0 0 0 1 1
( -672 -256 -16 ) ( -672 -256 -15 ) ( -671 -256 -16 ) ProtoWallsOrange -16 0 0 1 1
( -672 -256 -16 ) ( -671 -256 -16 ) ( -672 -255 -16 ) ProtoWallsOrange -16 0 0 1 1
( -480 -240 96 ) ( -480 -239 96 ) ( -479 -240 96 ) ProtoWallsOrange -16 0 0 1 1
( -480 -240 0 ) ( -479 -240 0 ) ( -480 -240 1 ) ProtoWallsOrange -16 0 0 1 1
( -480 -240 0 ) ( -480 -240 1 ) ( -480 -239 0 ) ProtoWallsOrange 0 0 0 1 1
}
// brush 9
{
( -416 -400 -16 ) ( -416 -399 -16 ) ( -416 -400 -15 ) ProtoWallsOrange 16 0 0 1 1
( -496 -400 -16 ) ( -496 -400 -15 ) ( -495 -400 -16 ) ProtoWallsOrange 0 0 0 1 1
( -496 -400 -16 ) ( -495 -400 -16 ) ( -496 -399 -16 ) ProtoWallsOrange 0 -16 0 1 1
( -304 -384 96 ) ( -304 -383 96 ) ( -303 -384 96 ) ProtoWallsOrange 0 -16 0 1 1
( -304 -384 0 ) ( -303 -384 0 ) ( -304 -384 1 ) ProtoWallsOrange 0 0 0 1 1
( -304 -384 0 ) ( -304 -384 1 ) ( -304 -383 0 ) ProtoWallsOrange 16 0 0 1 1
}
// brush 10
{
( -320 -384 80 ) ( -320 -383 80 ) ( -320 -384 81 ) ProtoWallsOrange 0 0 0 1 1
( -320 -384 80 ) ( -320 -384 81 ) ( -319 -384 80 ) ProtoWallsOrange 0 0 0 1 1
( -320 -384 64 ) ( -319 -384 64 ) ( -320 -383 64 ) ProtoWallsOrange 0 0 0 1 1
( -288 -352 96 ) ( -288 -351 96 ) ( -287 -352 96 ) ProtoWallsOrange 0 0 0 1 1
( -288 -320 96 ) ( -287 -320 96 ) ( -288 -320 97 ) ProtoWallsOrange 0 0 0 1 1
( -304 -352 96 ) ( -304 -352 97 ) ( -304 -351 96 ) ProtoWallsOrange 0 0 0 1 1
}
// brush 11
{
( -208 -304 -16 ) ( -208 -303 -16 ) ( -208 -304 -15 ) ProtoWallsGrey 32 0 0 1 1
( -208 -304 -16 ) ( -208 -304 -15 ) ( -207 -304 -16 ) ProtoWallsGrey -16 0 0 1 1
( -208 -304 -16 ) ( -207 -304 -16 ) ( -208 -303 -16 ) ProtoWallsGrey -16 -32 0 1 1
( -144 -256 80 ) ( -144 -255 80 ) ( -143 -256 80 ) ProtoWallsGrey -16 -32 0 1 1
( -144 -208 0 ) ( -143 -208 0 ) ( -144 -208 1 ) ProtoWallsGrey -16 0 0 1 1
( -32 -256 0 ) ( -32 -256 1 ) ( -32 -255 0 ) ProtoWallsGrey 32 0 0 1 1
}
// brush 12
{
( -544 -384 96 ) ( -544 -383 96 ) ( -544 -384 97 ) ProtoWallsGrey 0 0 0 1 1
( -544 -384 96 ) ( -544 -384 97 ) ( -543 -384 96 ) ProtoWallsGrey 0 0 0 1 1
( -544 -384 96 ) ( -543 -384 96 ) ( -544 -383 96 ) ProtoWallsGrey 0 0 0 1 1
( -304 -256 112 ) ( -304 -255 112 ) ( -303 -256 112 ) ProtoWallsGrey 0 0 0 1 1
( -304 -256 112 ) ( -303 -256 112 ) ( -304 -256 113 ) ProtoWallsGrey 0 0 0 1 1
( -304 -256 112 ) ( -304 -256 113 ) ( -304 -255 112 ) ProtoWallsGrey 0 0 0 1 1
}
}
// entity 1
{
"classname" "enemy_spawn"
"origin" "-480 64 8"
}
// entity 2
{
"classname" "info_player_start"
"origin" "176 -224 8"
}
// entity 3
{
"classname" "enemy_spawn"
"origin" "-480 -16 8"
}
// entity 4
{
"classname" "enemy_spawn"
"origin" "-352 -288 8"
}

View File

@ -1,46 +0,0 @@
const std = @import("std");
const graphics = @import("graphics");
const core = @import("core");
const platform = @import("platform");
const QuakeMap = graphics.QuakeMap;
test "simple_integration" {
// this doesn't really do anything other than just a simple compile check
std.debug.print("sizeof NeonVkContext = {d}\n", .{@sizeOf(graphics.NeonVkContext)});
std.debug.print("sizeof triangle_mesh_vert.ObjectData = {d}\n", .{@sizeOf(graphics.vk_renderer.triangle_mesh_vert.ObjectData)});
}
test "renderthread queue" {
const allocator = std.testing.allocator;
try core.start_module(.{}, .{}, allocator);
defer core.shutdown_module(allocator);
}
test "quake map loading" {
// const TestMap = @embedFile("testmap.map");
const testmapFile =
\\{
\\"spawnflags" "0"
\\"classname" "worldspawn"
\\"wad" "E:\q1maps\Q.wad"
\\{
\\( 256 64 16 ) ( 256 64 0 ) ( 256 0 16 ) mmetal1_2 0 0 0 1 1
\\( 0 0 0 ) ( 0 64 0 ) ( 0 0 16 ) mmetal1_2 0 0 0 1 1
\\( 64 256 16 ) ( 0 256 16 ) ( 64 256 0 ) mmetal1_2 0 0 0 1 1
\\( 0 0 0 ) ( 0 0 16 ) ( 64 0 0 ) mmetal1_2 0 0 0 1 1
\\( 64 64 0 ) ( 64 0 0 ) ( 0 64 0 ) mmetal1_2 0 0 0 1 1
\\( 0 0 -64 ) ( 64 0 -64 ) ( 0 64 -64 ) mmetal1_2 0 0 0 1 1
\\}
\\}
\\{
\\"spawnflags" "0"
\\"classname" "info_player_start"
\\"origin" "32 32 24"
\\}
;
var err: QuakeMap.ErrorInfo = undefined;
var map = try QuakeMap.read(std.testing.allocator, testmapFile, &err);
defer map.deinit();
}

View File

@ -1,2 +0,0 @@
plunder these ones for features to
implement in rend

View File

@ -1,52 +0,0 @@
const std = @import("std");
const SpirvReflect = @import("SpirvReflect");
pub fn addLib(b: *std.Build, exe: *std.Build.Step.Compile, comptime packagePath: []const u8, cflags: []const []const u8) void {
_ = cflags;
exe.addIncludePath(b.path(packagePath ++ "/papyrus/"));
exe.addCSourceFile(.{ .file = b.path(packagePath ++ "/papyrus/compat.cpp"), .flags = &.{""} });
}
const depList = [_][]const u8{
"core",
"assets",
"graphics",
"platform",
"papyrus",
"vulkan",
};
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const mod = b.addModule("ui", .{
.target = target,
.optimize = optimize,
.root_source_file = b.path("src/ui.zig"),
});
for (depList) |depName| {
const dep = b.dependency(depName, .{ .target = target, .optimize = optimize });
mod.addImport(depName, dep.module(depName));
}
const spirvGen = SpirvReflect.SpirvGenerator2.init(b, .{ .optimize = optimize });
spirvGen.addShader(mod, b.path("shaders/PapyrusRect.vert"), "papyrus_vk_vert");
spirvGen.addShader(mod, b.path("shaders/PapyrusRect.frag"), "papyrus_vk_frag");
spirvGen.addShader(mod, b.path("shaders/FontSDF.vert"), "FontSDF_vert");
spirvGen.addShader(mod, b.path("shaders/FontSDF.frag"), "FontSDF_frag");
const test_step = b.step("test", "run unit tests for ui");
const tests = b.addTest(.{
.target = target,
.optimize = optimize,
.root_source_file = b.path("tests/tests.zig"),
});
tests.root_module.addImport("ui", mod);
const runArtifact = b.addRunArtifact(tests);
test_step.dependOn(&runArtifact.step);
b.installArtifact(tests);
}

View File

@ -1,17 +0,0 @@
.{
.name = "ui",
.version = "0.0.0",
.dependencies = .{
.vulkan = .{ .path = "../../lib/vulkan" },
.SpirvReflect = .{ .path = "../../lib/spirv-reflect-zig" },
.papyrus = .{ .path = "../papyrus" },
.assets = .{ .path = "../assets" },
.platform = .{ .path = "../platform" },
.core = .{ .path = "../core" },
.graphics = .{ .path = "../graphics" },
},
.paths = .{
"",
},
}

View File

@ -1,69 +0,0 @@
#version 460
layout (location = 0) in vec4 color;
layout (location = 1) in vec2 texCoord;
layout (location = 2) flat in int instanceId;
layout (location = 3) in vec2 pixelPosition;
layout (location = 0) out vec4 outFragColor;
layout (set = 1, binding = 0) uniform sampler2D tex;
#include "FontSDFShared.glsl"
#include "FragmentHelpers.glsl"
void main() {
vec4 tex = texture(tex, texCoord);
uint isSdf = fontBuffer.fontInfo[instanceId].isSdf;
vec2 position = fontBuffer.fontInfo[instanceId].position;
vec2 size = fontBuffer.fontInfo[instanceId].size;
if(!scissor(pixelPosition, position, size))
{
discard;
}
if(isSdf == 1)
{
float dist = tex.r;
float width = fwidth(dist);
vec4 textColor = clamp(color, 0.0, 1.0);
float outerEdge = 1.0f - (120.0f / 255.0f);
float alpha = contour(dist, outerEdge, width);
float dscale = 0.354; // half of 1/sqrt2; you can play with this
vec2 uv = texCoord.xy;
vec2 duv = dscale * (dFdx(uv) + dFdy(uv));
vec4 box = vec4(uv - duv, uv + duv);
float asum = getSample(box.xy, outerEdge, width)
+ getSample(box.zw, outerEdge, width)
+ getSample(box.xw, outerEdge, width)
+ getSample(box.zy, outerEdge, width);
// weighted average, with 4 extra points having 0.5 weight each,
// so 1 + 0.5*4 = 3 is the divisor
alpha = (alpha + 0.5 * asum) / 3.0;
textColor = vec4(color.xyz, alpha);//textColor.* alpha);
textColor.xyz = pow(textColor.xyz, vec3(2.2)); // gamma correction
// Premultiplied alpha output.
outFragColor = textColor;
}
else {
float alpha = 1.0;
float gray = dot(color.xyz, vec3(0.2126, 0.7152, 0.0722));
outFragColor = vec4(color.xyz , pow(tex.x / gray, 1/(2.2)) );//textColor.* alpha);
//outFragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
/* debug test.
if(!rect(pixelPosition, position, size))
{
outFragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
*/
}

View File

@ -1,28 +0,0 @@
#version 460
layout(location = 0) in vec3 texPosition;
layout(location = 1) in vec3 texNormal;
layout(location = 2) in vec4 texColor;
layout(location = 3) in vec2 texCoord;
layout (location = 0) out vec4 fragColor;
layout (location = 1) out vec2 texCoords;
layout (location = 2) out int instanceId;
layout (location = 3) out vec2 pixelPosition;
#include "FontSDFShared.glsl"
void main()
{
vec2 pos = fontBuffer.fontInfo[gl_BaseInstance].position;
vec2 size = fontBuffer.fontInfo[gl_BaseInstance].size;
vec2 t = texPosition.xy + pos;
pixelPosition = t;
//gl_Position = vec4(( (texPosition.xy + pos) / PushConstants.extent) * 2 + vec2(-1.0f, -1.0f), texPosition.z, 1.0);
gl_Position = vec4((t / PushConstants.extent) * 2 + vec2(-1.0f, -1.0f), texPosition.z, 1.0);
texCoords = texCoord;
fragColor = texColor;
instanceId = gl_BaseInstance;
}

View File

@ -1,15 +0,0 @@
struct FontInfo {
vec2 position; // 8 bytes alignment 0
vec2 size; // 8 bytes alignment 8
uint isSdf; // 4 bytes 16
uint pad0; // 4 bytes
vec2 pad2; // 8 bytes
};
layout(std140, set = 0, binding = 0) readonly buffer FontInfoBuffer{
FontInfo fontInfo[];
} fontBuffer;
layout (push_constant) uniform constants {
vec2 extent;
} PushConstants;

View File

@ -1,39 +0,0 @@
float median(float r, float g, float b)
{
return max(min(r, g), min(max(r, g), b));
}
float contour(float dist, float edge, float width) {
return clamp(smoothstep(edge - width, edge + width, dist), 0.0, 1.0);
}
float getSample(vec2 texCoord, float edge, float width) {
return contour(texture(tex, texCoord).r, edge, width);
}
bool scissor(vec2 position, vec2 topleft, vec2 size)
{
if(position.x >= topleft.x && position.x <= topleft.x + size.x &&
position.y >= topleft.y && position.y <= topleft.y + size.y )
{
return true;
}
return false;
}
bool rect(vec2 position, vec2 topleft, vec2 size)
{
if(position.x >= topleft.x && position.x <= topleft.x + size.x &&
position.y >= topleft.y && position.y <= topleft.y + size.y )
{
return true;
}
return false;
}
bool somewhatEqual(float left, float right)
{
return distance(left, right) < 1.0;
}

View File

@ -1,111 +0,0 @@
#version 460
//shader input
layout (location = 0) in vec4 fragColor;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in vec2 panelPixelPosition; // relative to the topleft
layout (location = 3) flat in int instanceId;
layout (location = 0) out vec4 outFragColor;
layout (set = 1, binding = 0) uniform sampler2D tex;
#include "PapyrusRectShared.glsl"
#include "FragmentHelpers.glsl"
void main()
{
vec2 imageSize = objectBuffer.objects[instanceId].imageSize;
vec4 rounding = objectBuffer.objects[instanceId].rounding;
vec4 borderColor = objectBuffer.objects[instanceId].borderColor;
float borderWidth = objectBuffer.objects[instanceId].borderWidth;
float alpha = fragColor.w;
uint usesImage = objectBuffer.objects[instanceId].flags & 1;
// check to discard topleft
vec3 color = fragColor.xyz;
if(panelPixelPosition.x < rounding.x && panelPixelPosition.y < rounding.y)
{
float dist = distance(panelPixelPosition, vec2(rounding.x, rounding.x));
if(dist > (rounding.x ))
{
discard;
}
else if(somewhatEqual(dist, rounding.x))
{
color = borderColor.xyz;
alpha = borderColor.w;
}
}
// top right
if(panelPixelPosition.x > imageSize.x - rounding.y && panelPixelPosition.y < rounding.y )
{
float dist = distance(panelPixelPosition, vec2(imageSize.x - rounding.y, rounding.y));
if(dist > rounding.y)
{
discard;
}
else if(somewhatEqual(dist, rounding.y))
{
color = borderColor.xyz;
alpha = borderColor.w;
}
}
// bottom Left
if(panelPixelPosition.x < rounding.x && panelPixelPosition.y > imageSize.y - rounding.y)
{
float dist = distance(panelPixelPosition, vec2(rounding.x, imageSize.y - rounding.y));
if(dist > rounding.y)
{
discard;
}
else if(somewhatEqual(dist, rounding.y))
{
color = borderColor.xyz;
alpha = borderColor.w;
}
}
// bottom right
if(panelPixelPosition.x > imageSize.x - rounding.a && imageSize.y - panelPixelPosition.y < rounding.a )
{
float dist = distance(panelPixelPosition, vec2(imageSize.x - rounding.x, imageSize.y - rounding.y));
if(dist > rounding.y)
{
discard;
}
else if(somewhatEqual(dist, rounding.y))
{
color = borderColor.xyz;
alpha = borderColor.w;
}
}
// check to discard topright
// determine border colors
if( panelPixelPosition.x < borderWidth
|| panelPixelPosition.x > imageSize.x - borderWidth
|| panelPixelPosition.y < borderWidth
|| panelPixelPosition.y > imageSize.y - borderWidth
)
{
color = borderColor.xyz;
alpha = borderColor.w;
}
// scale the color
if(usesImage > 0)
{
vec4 sampledColor = texture(tex, vec2(texCoord.x, 1 - texCoord.y));
outFragColor = vec4(sampledColor.rgb, sampledColor.a * alpha);
}
else
{
outFragColor = vec4(pow(color, vec3(2.2)), alpha);
}
}

View File

@ -1,46 +0,0 @@
//we will be using glsl version 4.5 syntax
#version 460
layout (location = 0) in vec3 vPosition;
layout (location = 1) in vec3 vNormal;
layout (location = 2) in vec4 vColor;
layout (location = 3) in vec2 vTexCoord;
layout (location = 0) out vec4 outColor;
layout (location = 1) out vec2 texCoord;
layout (location = 2) out vec2 panelPixelPosition;
layout (location = 3) out int instanceId;
#include "PapyrusRectShared.glsl"
void main()
{
vec2 imagePosition = objectBuffer.objects[gl_BaseInstance].imagePosition;
vec2 imageSize = objectBuffer.objects[gl_BaseInstance].imageSize;
vec2 anchor = objectBuffer.objects[gl_BaseInstance].anchorPoint;
vec2 scale = objectBuffer.objects[gl_BaseInstance].scale;
float alpha = objectBuffer.objects[gl_BaseInstance].alpha;
vec4 baseColor = objectBuffer.objects[gl_BaseInstance].baseColor;
vec2 finalSize = (imageSize / PushConstants.extent);
//float zLevel = objectBuffer.objects[gl_BaseInstance].zLevel;
vec2 finalPos = ((imagePosition / PushConstants.extent) * 2 - 1) - anchor * finalSize * scale;
outColor = baseColor;
//outColor = vec3(vColor.x, vColor.y, vColor.z);
vec4 fp = vec4(
finalPos.x + ( vPosition.x * finalSize.x * scale.x),
finalPos.y + (-vPosition.y * finalSize.y * scale.y),
vPosition.z, 1.0
);
//1.0);
gl_Position = fp;
//gl_Position = vec4( ((position.x) - 1.3) * 0.3 * 1.3, (-position.y + 0.05) * 1.3, position.z, 1.0); // + vec4(imagePosition, 0.0f, 1.0f);
texCoord = vec2(1 - vTexCoord.x, vTexCoord.y);
panelPixelPosition = (vPosition.xy - anchor) / 2 * imageSize;
panelPixelPosition.y = imageSize.y - panelPixelPosition.y;
instanceId = gl_BaseInstance;
}

View File

@ -1,23 +0,0 @@
// has to match what's in the vertex shader
struct ImageRenderData {
vec2 imagePosition;
vec2 imageSize;
vec2 anchorPoint;
vec2 scale;
float alpha;
float borderWidth;
uint flags;
vec4 baseColor;
vec4 rounding;
vec4 borderColor;
};
layout(std140, set = 0, binding = 0) readonly buffer ImageBufferObjects {
ImageRenderData objects[];
} objectBuffer;
layout (push_constant) uniform constants
{
vec2 extent;
} PushConstants;

View File

@ -1,740 +0,0 @@
gc: *graphics.NeonVkContext,
allocator: std.mem.Allocator,
pipeData: gpd.GpuPipeData = undefined,
materialName: core.Name = core.Name.MakeComptime("mat_papyrus"),
materialNameText: core.Name = core.Name.MakeComptime("mat_papyrus_text"),
material: *graphics.Material = undefined, // main material used for anything that isn't text
defaultTextureSet: vk.DescriptorSet,
textMaterial: *graphics.Material = undefined, // main material used for text
mappedBuffers: []gpd.GpuMappingData(ImageGpu) = undefined,
textImageBuffers: []gpd.GpuMappingData(FontInfo) = undefined,
indexBuffer: graphics.IndexBuffer = undefined,
drawList: papyrus.DrawList,
stringArena: std.heap.ArenaAllocator,
fontTexture: *graphics.Texture = undefined,
papyrusCtx: *papyrus.Context,
quad: *graphics.Mesh,
ssboCount: u32 = 0,
textSsboCount: u32 = 0,
time: f64 = 0,
textPipeData: gpd.GpuPipeData = undefined,
displayDemo: bool = true,
drawCommands: std.ArrayList(VkCommand),
textRenderer: *TextRenderer,
averageFrameTime: f64 = 0,
onDebugInfoBinding: usize = 0,
sharedData: [graphics.NumFrames]SharedData = undefined,
const std = @import("std");
const core = @import("core");
const memory = core.MemoryTracker;
const assets = @import("assets");
const graphics = @import("graphics");
const gpd = graphics.gpu_pipe_data;
const RenderThread = graphics.RenderThread;
const platform = @import("platform");
const papyrus = @import("papyrus");
const papyrus_vk_vert = @import("papyrus_vk_vert");
const papyrus_vk_frag = @import("papyrus_vk_frag");
const FontSDF_vert = @import("FontSDF_vert");
const FontSDF_frag = @import("FontSDF_frag");
const gl = @import("glslTypes");
const vk = @import("vulkan");
const tracy = core.tracy;
const Text = papyrus.Text;
const VkCommand = @import("VkPapyrusRenderCommand.zig").VkCommand;
const text_render = @import("text_render.zig");
const TextRenderer = text_render.TextRenderer;
const DisplayText = text_render.DisplayText;
const FontAtlasVk = text_render.FontAtlasVk;
const Key = papyrus.Event.Key;
pub const RawInputListenerVTable = platform.windowing.RawInputListenerInterface.from(@This());
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
pub const RendererInterfaceVTable = graphics.RendererInterface.from(@This());
pub const PushConstant = FontSDF_vert.constants;
pub const ImageGpu = papyrus_vk_vert.ImageRenderData;
pub const FontInfo = FontSDF_vert.FontInfo;
pub fn init(allocator: std.mem.Allocator) !*@This() {
const papyrusCtx = try papyrus.initialize(allocator);
const self = try allocator.create(@This());
self.* = .{
.allocator = allocator,
.gc = graphics.getContext(),
.papyrusCtx = papyrusCtx,
.quad = try allocator.create(graphics.Mesh),
.drawCommands = std.ArrayList(VkCommand).init(allocator),
.textRenderer = try TextRenderer.init(allocator, graphics.getContext(), papyrusCtx),
.drawList = papyrus.DrawList.init(allocator),
.stringArena = std.heap.ArenaAllocator.init(allocator),
.defaultTextureSet = undefined,
};
self.initShared();
self.onDebugInfoBinding = try core.addEngineDelegateBinding("onFrameDebugInfoEmitted", onFrameDebugInfo, self);
// core.engine_logs("PapyrusSystem init");
memory.MTPrintStatsDelta();
try platform.getInstance().installListener(self);
return self;
}
pub fn onFrameDebugInfo(ctx: *anyopaque, averageFrameTime: f64) core.EngineDataEventError!void {
const self: *@This() = @ptrCast(@alignCast(ctx));
self.averageFrameTime = averageFrameTime;
}
pub fn OnIoEvent(self: *@This(), event: platform.IOEvent) platform.InputListenerError!void {
try OnIoEvent_GLFW(self, event);
}
pub fn OnIoEvent_GLFW(self: *@This(), event: platform.IOEvent) platform.InputListenerError!void {
switch (event) {
.mousePosition => |mousePosition| {
_ = mousePosition;
},
.mouseButton => |mouseButton| {
var keycode: Key = .Unknown;
var eventType: papyrus.Event.PressedType = .onPressed;
switch (mouseButton.button) {
0 => {
// left click
keycode = Key.Mouse1;
},
1 => {
// right click
keycode = Key.Mouse2;
},
2 => {
// middle click
keycode = Key.Mouse3;
},
3 => {
// button 3
keycode = Key.Mouse4;
},
4 => {
// button 4
keycode = Key.Mouse5;
},
else => {},
}
switch (mouseButton.action) {
0 => {
eventType = .onReleased;
},
1 => {
eventType = .onPressed;
},
else => {},
}
// todo: use the right error code here
self.papyrusCtx.onKey(keycode, eventType) catch unreachable;
},
.windowResize => |e| {
const pi = platform.getInstance();
self.papyrusCtx.get(.{}).setSize(.{
.x = e.newSize.x / pi.contentScale.x,
.y = e.newSize.y / pi.contentScale.y,
});
},
.codepoint => |codepoint| {
try self.papyrusCtx.textEntry.sendCodePoint(@as(u32, codepoint));
},
.key => |keyEvent| {
var te = self.papyrusCtx.textEntry;
const actions = platform.glfw_defs.actions;
const keys = platform.glfw_defs.keys;
if (@as(actions, @enumFromInt(keyEvent.action)) == actions.Press or
@as(actions, @enumFromInt(keyEvent.action)) == actions.Repeat)
{
// reference glfw3.h
switch (@as(keys, @enumFromInt(keyEvent.key))) {
keys.Escape => {
try te.sendEscape();
},
keys.Enter => {
try te.sendEnter();
},
keys.Tab => {
try te.sendTab();
},
keys.Backspace => {
try te.sendBackspace();
},
keys.Delete => {
try te.sendDelete();
},
keys.Right => {
try te.sendRight();
},
keys.Left => {
try te.sendLeft();
},
keys.Up => {
try te.sendUp();
},
keys.Down => {
try te.sendDown();
},
keys.Pageup => {
try te.sendPageup();
},
keys.Pagedown => {
try te.sendPagedown();
},
keys.Home => {
try te.sendHome();
},
keys.End => {
try te.sendEnd();
},
else => {},
}
}
},
else => {},
}
}
const t_white_name = core.StaticName("t_white");
pub fn setup(self: *@This(), gc: *graphics.NeonVkContext) !void {
core.ui_log("Papyrus Subsystem setup {x}", .{@intFromPtr(self)});
self.gc = gc;
try self.preparePipeline();
try self.setupMeshes();
try self.gc.registerRendererPlugin(self);
self.defaultTextureSet = self.gc.textureSets.get(t_white_name.handle()).?;
self.mappedBuffers = try self.pipeData.mapBuffers(self.gc, ImageGpu, 0);
self.textImageBuffers = try self.textPipeData.mapBuffers(self.gc, FontInfo, 0);
// core.ui_log("Mapping buffers.", .{});
const extent = self.gc.actual_extent;
const pi = platform.getInstance();
self.papyrusCtx.get(.{}).setSize(.{
.x = @as(f32, @floatFromInt(extent.width)) / pi.contentScale.x,
.y = @as(f32, @floatFromInt(extent.height)) / pi.contentScale.y,
});
}
pub fn preDraw(self: *@This(), frameId: usize) void {
_ = self;
_ = frameId;
}
// todo: this thing should be optional
pub fn onBindObject(self: *@This(), objectHandle: core.ObjectHandle, objectIndex: usize, cmd: vk.CommandBuffer, frameIndex: usize) void {
_ = self;
_ = objectHandle;
_ = objectIndex;
_ = cmd;
_ = frameIndex;
}
// Uploads a new primitive mesh and an index buffer to the gpu.
fn setupMeshes(self: *@This()) !void {
self.quad.* = graphics.Mesh.init(self.gc, self.allocator);
try self.quad.vertices.resize(4);
var indexBuffer: [6]u32 = undefined;
self.quad.vertices.items[0].position = .{ .x = 1, .y = 1, .z = 0 }; // bot right
self.quad.vertices.items[1].position = .{ .x = 1, .y = -1, .z = 0 }; // top right
self.quad.vertices.items[2].position = .{ .x = -1, .y = -1, .z = 0 }; // top left
self.quad.vertices.items[3].position = .{ .x = -1, .y = 1, .z = 0 }; // bot left
self.quad.vertices.items[0].uv = .{ .x = 1.0, .y = 1.0 };
self.quad.vertices.items[1].uv = .{ .x = 1.0, .y = 0.0 };
self.quad.vertices.items[2].uv = .{ .x = 0.0, .y = 0.0 };
self.quad.vertices.items[3].uv = .{ .x = 0.0, .y = 1.0 };
indexBuffer[0] = 0;
indexBuffer[1] = 1;
indexBuffer[2] = 2;
indexBuffer[3] = 2;
indexBuffer[4] = 3;
indexBuffer[5] = 0;
self.indexBuffer = try graphics.IndexBuffer.uploadIndexBuffer(self.gc, &indexBuffer, self.allocator);
try self.quad.upload(self.gc);
}
var lastEventCount: usize = 0;
var displayEventsPerSecond: f64 = 0;
pub fn tick(self: *@This(), deltaTime: f64) void {
self.time += deltaTime;
const cursor = platform.getInstance().inputState.mousePos;
// TODO, use OnIoEvent, but ehh this is fine.
self.papyrusCtx.setCursorLocation(.{
.x = @floatCast(cursor.x),
.y = @floatCast(cursor.y),
});
self.papyrusCtx.tick(deltaTime) catch unreachable;
if (memory.MTGet()) |mt| {
const eventsPerFrame = @as(f64, @floatFromInt(mt.eventsCount - lastEventCount));
displayEventsPerSecond = (displayEventsPerSecond + eventsPerFrame / 60.0) - displayEventsPerSecond / 60.0;
self.papyrusCtx.pushDebugText("memory used: {d:.3}MB {d} allocations ({d} events per frame)", .{
@as(f32, @floatFromInt(mt.totalAllocSize)) / 1e6,
mt.allocationsCount,
eventsPerFrame,
}) catch {};
lastEventCount = mt.eventsCount;
}
if (self.gc.vulkanValidation) {
self.papyrusCtx.pushDebugText("vulkan validation: ON", .{}) catch unreachable;
}
self.papyrusCtx.pushDebugText("ecs entities: {d}", .{core.getRegistry().baseSet.count()}) catch unreachable;
self.papyrusCtx.pushDebugText("frameTime (ms): {d:.4} fps: {d:.3} engine uptime: {d:.3}", .{ self.averageFrameTime * 1000.0, 1.0 / self.averageFrameTime, core.getEngineUptime() }) catch unreachable;
self.papyrusCtx.pushDebugText(" systems (ms): {d:.4}", .{
core.getEngine().systemsThreadTime * 1000.0,
}) catch unreachable;
self.papyrusCtx.pushDebugText(" renderthread (ms): {d:.4}", .{core.getEngine().renderThreadTime * 1000.0}) catch unreachable;
// for(core.getEngine().)
}
pub fn buildTextPipeline(self: *@This()) !void {
var gpdBuilder = gpd.GpuPipeDataBuilder.init(self.allocator, self.gc);
gpdBuilder.objectCount = 64;
try gpdBuilder.addBufferBinding(
FontInfo,
.storage_buffer,
.{ .vertex_bit = true, .fragment_bit = true },
.storageBuffer,
);
self.textPipeData = try gpdBuilder.build("Papyrus-Text");
defer gpdBuilder.deinit();
// const vert_spv = try graphics.loadSpv(self.allocator, "FontSDF_vert.spv");
// defer self.allocator.free(vert_spv);
// const frag_spv = try graphics.loadSpv(self.allocator, "FontSDF_frag.spv");
// defer self.allocator.free(frag_spv);
const vert_spv = FontSDF_vert.spv();
const frag_spv = FontSDF_frag.spv();
var builder = try graphics.NeonVkPipelineBuilder.init(
self.gc.dev,
self.gc.vkd,
self.gc.allocator,
self.gc.vkAllocator,
vert_spv,
frag_spv,
);
defer builder.deinit();
try builder.add_mesh_description();
try builder.add_layout(self.textPipeData.descriptorSetLayout);
try builder.add_layout(self.gc.singleTextureSetLayout);
try builder.add_depth_stencil();
try builder.add_push_constant_custom(PushConstant);
try builder.init_triangle_pipeline(self.gc.actual_extent);
self.textMaterial = try self.allocator.create(graphics.Material);
self.textMaterial.* = graphics.Material{
.materialName = self.materialNameText,
.pipeline = (try builder.build(self.gc.renderPass)).?,
.layout = builder.pipelineLayout,
};
try self.gc.add_material(self.textMaterial);
}
pub fn buildImagePipeline(self: *@This()) !void {
var spriteDataBuilder = gpd.GpuPipeDataBuilder.init(self.allocator, self.gc);
try spriteDataBuilder.addBufferBinding(
ImageGpu,
.storage_buffer,
.{ .vertex_bit = true, .fragment_bit = true },
.storageBuffer,
);
self.pipeData = try spriteDataBuilder.build("Papyrus");
defer spriteDataBuilder.deinit();
const vert_spv = papyrus_vk_vert.spv();
const frag_spv = papyrus_vk_frag.spv();
var builder = try graphics.NeonVkPipelineBuilder.init(
self.gc.dev,
self.gc.vkd,
self.gc.allocator,
self.gc.vkAllocator,
vert_spv,
frag_spv,
);
defer builder.deinit();
try builder.add_mesh_description();
try builder.add_layout(self.pipeData.descriptorSetLayout);
try builder.add_layout(self.gc.singleTextureSetLayout);
try builder.add_depth_stencil();
try builder.add_push_constant_custom(PushConstant);
try builder.init_triangle_pipeline(self.gc.actual_extent);
const material = try self.gc.allocator.create(graphics.Material);
material.* = graphics.Material{
.materialName = self.materialName,
.pipeline = (try builder.build(self.gc.renderPass)).?,
.layout = builder.pipelineLayout,
};
try self.gc.add_material(material);
self.material = material;
}
pub fn preparePipeline(self: *@This()) !void {
try self.buildTextPipeline();
try self.buildImagePipeline();
}
pub fn uploadSSBOData(self: *@This(), frameId: usize, drawList: *const papyrus.DrawList) !void {
var z = tracy.ZoneN(@src(), "Uploading SSBOs");
defer z.End();
var imagesGpu = self.mappedBuffers[frameId].objects;
var imagesText = self.textImageBuffers[frameId].objects;
self.textSsboCount = 0;
self.ssboCount = 0;
var textFrameContext = self.textRenderer.startRendering();
for (drawList.items) |drawCmd| {
switch (drawCmd.primitive) {
.Rect => |rect| {
imagesGpu[self.ssboCount] = ImageGpu{
.imagePosition = .{ .x = rect.tl.x, .y = rect.tl.y },
.imageSize = .{ .x = rect.size.x, .y = rect.size.y },
.anchorPoint = .{ .x = -1.0, .y = -1.0 },
.scale = .{ .x = 1.0, .y = 1.0 },
.alpha = 1.0,
.pad0 = std.mem.zeroes([4]u8),
.baseColor = .{
.x = rect.backgroundColor.r,
.y = rect.backgroundColor.g,
.z = rect.backgroundColor.b,
.w = rect.backgroundColor.a,
},
.rounding = .{
.x = rect.rounding.tl,
.y = rect.rounding.tr,
.z = rect.rounding.bl,
.w = rect.rounding.br,
},
.borderColor = .{
.x = rect.borderColor.r,
.y = rect.borderColor.g,
.z = rect.borderColor.b,
.w = rect.borderColor.a,
},
.borderWidth = rect.borderWidth,
.flags = 0,
};
var imageSet: ?vk.DescriptorSet = null;
if (rect.imageRef) |_imageRef| {
var ref = _imageRef;
if (self.gc.textureSets.get(ref.handle())) |maybeImageSet| {
imageSet = maybeImageSet;
imagesGpu[self.ssboCount].flags = 1;
}
}
try self.drawCommands.append(.{
.image = .{ .index = self.ssboCount, .imageSet = imageSet },
});
// core.ui_log("drawCmd: {any} {any} {any}", .{
// drawCmd.node,
// imagesGpu[self.ssboCount].imagePosition,
// imagesGpu[self.ssboCount].imageSize,
// });
self.ssboCount += 1;
},
.Text => |text| {
const nextDisplay = self.textRenderer.getNextSlot(text.text.utf8.len, &textFrameContext);
var textDisplay: *DisplayText = undefined;
if (nextDisplay.small) {
textDisplay = self.textRenderer.smallDisplays.items[nextDisplay.index];
} else {
textDisplay = self.textRenderer.displays.items[nextDisplay.index];
}
textDisplay.displaySize = text.textSize;
textDisplay.renderMode = text.renderMode;
textDisplay.boxSize = .{ .x = text.size.x, .y = text.size.y };
textDisplay.color = text.color;
textDisplay.position = .{ .x = text.tl.x, .y = text.tl.y };
// TODO this is really bad and confusing.
// The renderer should not be storing a hash on the papyrus resource.
if (text.rendererHash != 0) {
textDisplay.atlas = self.textRenderer.fonts.get(text.rendererHash).?;
}
textDisplay.setString(&text.text.utf8);
try textDisplay.updateMesh(text.flags.setSourceGeometry);
self.papyrusCtx.get(drawCmd.node).textRenderedSize = textDisplay.renderedSize;
if (text.flags.setSourceGeometry) {
self.papyrusCtx.textEntry.trg = textDisplay.renderedGeo;
}
// core.ui_log("nextDisplay = {any}, font = {d} sdf={any} ssbo={d}", .{
// nextDisplay,
// text.rendererHash,
// textDisplay.atlas.atlas.isSDF,
// self.textSsboCount,
// });
imagesText[self.textSsboCount] = .{
.isSdf = if (textDisplay.atlas.atlas.isSDF) 1 else 0,
.position = .{ .x = textDisplay.position.x, .y = textDisplay.position.y },
.size = .{ .x = textDisplay.boxSize.x, .y = textDisplay.boxSize.y },
.pad0 = 0,
.pad2 = undefined,
};
try self.drawCommands.append(.{ .text = .{
.index = nextDisplay.index,
.small = nextDisplay.small,
.ssbo = self.textSsboCount,
} });
self.textSsboCount += 1;
},
}
}
}
pub fn rtPostDraw(self: *@This(), rt: *RenderThread, cmd: vk.CommandBuffer, frameIndex: u32) void {
const shared = self.getShared(frameIndex);
self.drawCommands.clearRetainingCapacity();
const rtShared = rt.getShared(frameIndex);
shared.lock.lock();
self.uploadSSBOData(frameIndex, &shared.drawList) catch unreachable;
shared.lock.unlock();
var vertexBufferOffset: u64 = 0;
var pushConstant = PushConstant{
.extent = .{
.x = rtShared.extent.x,
.y = rtShared.extent.y,
},
};
for (self.drawCommands.items) |command| {
switch (command) {
.text => |t| {
self.gc.vkd.cmdPushConstants(cmd, self.textMaterial.layout, .{ .vertex_bit = true, .fragment_bit = true }, 0, @sizeOf(PushConstant), &pushConstant);
if (t.small) {
var drawText = self.textRenderer.smallDisplays.items[t.index];
drawText.draw(frameIndex, cmd, self.textMaterial, t.ssbo, self.textPipeData);
} else {
var drawText = self.textRenderer.displays.items[t.index];
drawText.draw(frameIndex, cmd, self.textMaterial, t.ssbo, self.textPipeData);
}
},
.image => |img| {
self.gc.vkd.cmdPushConstants(cmd, self.material.layout, .{ .vertex_bit = true, .fragment_bit = true }, 0, @sizeOf(PushConstant), &pushConstant);
const index = img.index;
self.gc.vkd.cmdBindPipeline(cmd, .graphics, self.material.pipeline);
self.gc.vkd.cmdBindVertexBuffers(cmd, 0, 1, @ptrCast(&self.quad.buffer.buffer), @ptrCast(&vertexBufferOffset));
self.gc.vkd.cmdBindIndexBuffer(cmd, self.indexBuffer.buffer.buffer, 0, .uint32);
self.gc.vkd.cmdBindDescriptorSets(cmd, .graphics, self.material.layout, 0, 1, self.pipeData.getDescriptorSet(frameIndex), 0, undefined);
if (img.imageSet) |imageSet| {
self.gc.vkd.cmdBindDescriptorSets(cmd, .graphics, self.material.layout, 1, 1, @ptrCast(&imageSet), 0, undefined);
} else {
self.gc.vkd.cmdBindDescriptorSets(cmd, .graphics, self.material.layout, 1, 1, @ptrCast(&self.defaultTextureSet), 0, undefined);
}
self.gc.vkd.cmdDrawIndexed(cmd, @as(u32, @intCast(self.indexBuffer.indices.len)), 1, 0, 0, index);
},
}
}
}
pub fn getShared(self: *@This(), fi: u32) *SharedData {
return &self.sharedData[fi];
}
pub fn sendShared(self: *@This(), frameIndex: u32) void {
const z1 = tracy.ZoneN(@src(), "PapryusIntegration - UploadingShared");
defer z1.End();
const shared = self.getShared(frameIndex);
shared.lock.lock();
defer shared.lock.unlock();
self.papyrusCtx.makeDrawList(&shared.drawList, &shared.stringArena) catch unreachable;
}
pub fn postDraw(self: *@This(), cmd: vk.CommandBuffer, frameIndex: usize, frameTime: f64) void {
_ = frameTime;
var z = tracy.ZoneN(@src(), "Papyrus post draw");
defer z.End();
var vertexBufferOffset: u64 = 0;
if (!self.displayDemo) {
return;
}
var z1 = tracy.ZoneN(@src(), "Papyrus Making Draw List");
self.papyrusCtx.makeDrawList(&self.drawList, &self.stringArena) catch unreachable;
z1.End();
self.drawCommands.clearRetainingCapacity();
self.uploadSSBOData(frameIndex, &self.drawList) catch unreachable;
var pushConstant = PushConstant{
.extent = .{
.x = @as(f32, @floatFromInt(self.gc.extent.width)),
.y = @as(f32, @floatFromInt(self.gc.extent.height)),
},
};
for (self.drawCommands.items) |command| {
switch (command) {
.text => |t| {
self.gc.vkd.cmdPushConstants(cmd, self.textMaterial.layout, .{ .vertex_bit = true, .fragment_bit = true }, 0, @sizeOf(PushConstant), &pushConstant);
if (t.small) {
var drawText = self.textRenderer.smallDisplays.items[t.index];
drawText.draw(frameIndex, cmd, self.textMaterial, t.ssbo, self.textPipeData);
} else {
var drawText = self.textRenderer.displays.items[t.index];
drawText.draw(frameIndex, cmd, self.textMaterial, t.ssbo, self.textPipeData);
}
},
.image => |img| {
self.gc.vkd.cmdPushConstants(cmd, self.material.layout, .{ .vertex_bit = true, .fragment_bit = true }, 0, @sizeOf(PushConstant), &pushConstant);
const index = img.index;
self.gc.vkd.cmdBindPipeline(cmd, .graphics, self.material.pipeline);
self.gc.vkd.cmdBindVertexBuffers(cmd, 0, 1, @ptrCast(&self.quad.buffer.buffer), @ptrCast(&vertexBufferOffset));
self.gc.vkd.cmdBindIndexBuffer(cmd, self.indexBuffer.buffer.buffer, 0, .uint32);
self.gc.vkd.cmdBindDescriptorSets(cmd, .graphics, self.material.layout, 0, 1, self.pipeData.getDescriptorSet(frameIndex), 0, undefined);
if (img.imageSet) |imageSet| {
self.gc.vkd.cmdBindDescriptorSets(cmd, .graphics, self.material.layout, 1, 1, @ptrCast(&imageSet), 0, undefined);
} else {
self.gc.vkd.cmdBindDescriptorSets(cmd, .graphics, self.material.layout, 1, 1, @ptrCast(&self.defaultTextureSet), 0, undefined);
}
self.gc.vkd.cmdDrawIndexed(cmd, @as(u32, @intCast(self.indexBuffer.indices.len)), 1, 0, 0, index);
},
}
}
}
pub fn shutdown(self: *@This()) void {
self.gc.vkd.deviceWaitIdle(self.gc.dev) catch unreachable;
core.ui_logs("Shutting down UI");
for (self.mappedBuffers) |*mapped| {
mapped.unmap(self.gc);
}
for (self.textImageBuffers) |*mapped| {
mapped.unmap(self.gc);
}
self.drawCommands.deinit();
self.stringArena.deinit();
self.quad.deinit(self.gc);
self.allocator.destroy(self.quad);
self.gc.allocator.free(self.mappedBuffers);
self.gc.allocator.free(self.textImageBuffers);
self.textRenderer.deinit(self.allocator);
self.pipeData.deinit(self.allocator, self.gc);
self.textPipeData.deinit(self.allocator, self.gc);
self.indexBuffer.deinit(self.gc);
self.drawList.deinit();
self.papyrusCtx.deinit();
self.deinitShared();
core.ui_logs("finished shutting down ui");
}
pub fn deinit(self: *@This()) void {
self.shutdown();
self.allocator.destroy(self);
}
pub fn processEvents(self: *@This(), frameNumber: u64) core.EngineDataEventError!void {
_ = frameNumber;
_ = self;
}
const SharedData = struct {
lock: std.Thread.Mutex,
drawList: papyrus.DrawList,
stringArena: std.heap.ArenaAllocator,
};
pub fn initShared(self: *@This()) void {
for (&self.sharedData) |*s| {
s.* = .{
.lock = .{},
.drawList = papyrus.DrawList.init(self.allocator),
.stringArena = std.heap.ArenaAllocator.init(self.allocator),
};
}
}
pub fn deinitShared(self: *@This()) void {
for (&self.sharedData) |*s| {
s.drawList.deinit();
s.stringArena.deinit();
}
}

View File

@ -1,14 +0,0 @@
const std = @import("std");
const vk = @import("vulkan");
pub const VkCommand = union(enum(u8)) {
image: struct {
index: u32,
imageSet: ?vk.DescriptorSet,
},
text: struct {
index: u32,
small: bool,
ssbo: u32,
},
};

View File

@ -1,459 +0,0 @@
const std = @import("std");
const vk = @import("vulkan");
const core = @import("core");
const graphics = @import("graphics");
const memory = core.MemoryTracker;
const papyrus = @import("papyrus");
const gpd = graphics.gpu_pipe_data;
const FontAtlas = papyrus.FontAtlas;
const DynamicMesh = graphics.DynamicMesh;
const ArrayListU = std.ArrayListUnmanaged;
const AutoHashMapU = std.AutoHashMapUnmanaged;
const Vector2f = core.Vector2f;
const Vectorf = core.Vectorf;
const Color = papyrus.Color;
pub const FontAtlasVk = struct {
g: *graphics.NeonVkContext,
allocator: std.mem.Allocator,
isDefault: bool = false,
atlas: *FontAtlas,
texture: *graphics.Texture = undefined,
textureSet: vk.DescriptorSet = undefined,
fontName: core.Name = undefined,
pub fn deinit(self: @This()) void {
_ = self;
}
pub fn init(
allocator: std.mem.Allocator,
g: *graphics.NeonVkContext,
) !@This() {
const self = @This(){
.allocator = allocator,
.atlas = undefined,
.g = g,
};
return self;
}
pub fn loadFont(self: *@This(), papyrusCtx: *papyrus.Context, fontPath: []const u8) !void {
self.atlas = try papyrusCtx.allocator.create(FontAtlas);
self.atlas.* = try FontAtlas.initFromFileSDF(papyrusCtx.allocator, fontPath, 64);
}
pub fn prepareFont(self: *@This(), fontName: core.Name) !void {
const pixels = try self.atlas.makeBitmapRGBA(self.allocator);
defer self.allocator.free(pixels);
const res = try graphics.createAndInstallTextureFromPixels(
fontName,
pixels,
.{ .x = self.atlas.atlasSize.x, .y = self.atlas.atlasSize.y },
self.g,
false,
);
self.atlas.cleanUp();
self.fontName = fontName;
self.texture = res.texture;
self.textureSet = res.descriptor;
}
};
pub const DisplayText = struct {
allocator: std.mem.Allocator,
g: *graphics.NeonVkContext, // ref
atlas: *FontAtlasVk, // ref
mesh: *DynamicMesh, // we own this
string: ?*const []const u8,
stringHash: u32 = 0xffffffff,
renderMode: papyrus.TextRenderMode,
displaySize: f32 = 24.0,
position: Vector2f = .{},
boxSize: Vector2f = .{ .x = 10, .y = 10 },
color: Color = .{ .r = 1.0, .g = 1.0, .b = 1.0 },
wordWrap: bool = true,
renderedSize: Vector2f = .{},
renderedGeo: *papyrus.TextRenderGeometry,
pub fn deinit(self: *@This()) void {
self.mesh.deinit();
self.allocator.destroy(self.mesh);
self.renderedGeo.destroy();
}
pub fn getHash(self: *@This()) u32 {
var hash: u32 = 5381;
// todo: swap the hash into a new function.
//
// walk up the string list until we are alignment = 4,
// sum everything using u32s
// sum up the missing chars at the end.
for (self.string.?) |c| {
hash = @mulWithOverflow(hash, 33)[0];
hash = @addWithOverflow(hash, @as(u32, @intCast(c)))[0];
}
hash = @addWithOverflow(hash, @as(u32, @bitCast(self.displaySize)))[0];
hash = @mulWithOverflow(hash, @as(u32, @bitCast(self.position.x)))[0];
hash = @addWithOverflow(hash, @as(u32, @bitCast(self.position.y)))[0];
hash = @mulWithOverflow(hash, @as(u32, @bitCast(self.boxSize.x)))[0];
hash = @mulWithOverflow(hash, @as(u32, @bitCast(self.boxSize.y)))[0];
const color = self.color;
hash = @mulWithOverflow(hash, @as(u32, @bitCast(color.r + color.g * 10 + color.b * 100)))[0];
return hash;
}
pub fn init(
allocator: std.mem.Allocator,
atlas: *FontAtlasVk,
opts: struct {
charLimit: u32 = 8192,
},
) !@This() {
const self = @This(){
.g = atlas.g,
.allocator = allocator,
.atlas = atlas,
.renderMode = .Simple,
.mesh = try graphics.DynamicMesh.init(atlas.g, atlas.g.allocator, .{
.maxVertexCount = opts.charLimit * 4,
.maxIndexCount = opts.charLimit * 4 * 6 / 4,
}),
.string = null,
.renderedGeo = try papyrus.TextRenderGeometry.create(allocator),
};
return self;
}
pub fn draw(
self: *@This(),
frameIndex: usize,
cmd: vk.CommandBuffer,
textMaterial: *graphics.Material,
ssboId: u32,
textPipeData: gpd.GpuPipeData,
) void {
var fontSet = self.atlas.textureSet;
var vkd = self.g.vkd;
var vertexBufferOffset: u64 = 0;
vkd.cmdBindPipeline(cmd, .graphics, textMaterial.pipeline);
vkd.cmdBindVertexBuffers(cmd, 0, 1, @ptrCast(&self.mesh.getVertexBuffer().buffer), @ptrCast(&vertexBufferOffset));
vkd.cmdBindIndexBuffer(cmd, self.mesh.getIndexBuffer().buffer, 0, .uint32);
vkd.cmdBindDescriptorSets(cmd, .graphics, textMaterial.layout, 0, 1, textPipeData.getDescriptorSet(frameIndex), 0, undefined);
vkd.cmdBindDescriptorSets(cmd, .graphics, textMaterial.layout, 1, 1, @ptrCast(&fontSet), 0, undefined);
vkd.cmdDrawIndexed(cmd, self.mesh.getIndexBufferLen(), 1, 0, 0, ssboId);
}
pub fn setMode(self: *@This(), mode: papyrus.TextParseMode) void {
self.renderMode = mode;
}
pub fn setPosition(self: *@This(), position: Vector2f) void {
self.position = position;
}
pub fn setBox(self: *@This(), boxSize: Vector2f) void {
self.boxSize = boxSize;
}
pub fn setString(self: *@This(), str: *const []const u8) void {
self.string = str;
}
const RenderState = struct {
xOffset: f32 = 0,
yOffset: f32 = 0,
};
pub fn updateMesh(self: *@This(), buildHitboxes: bool) !void {
_ = buildHitboxes;
self.mesh.clearVertices();
// ! not threadsafe...
// this might be really bad for stalls.
self.renderedGeo.lock();
defer self.renderedGeo.unlock();
try self.renderedGeo.resetAllLines();
const atlas = self.atlas.atlas;
const ratio = (self.displaySize) / atlas.fontSize;
const stride = @as(f32, @floatFromInt(atlas.glyphMetrics['l'].x)) * ratio;
if (self.string.?.len <= 0) {
return;
}
var xOffset: f32 = 0;
var yOffset: f32 = 0;
const fontHeight = @as(f32, @floatFromInt(atlas.glyphMetrics['l'].y)) * ratio;
self.renderedGeo.setCharHeight(fontHeight);
self.renderedGeo.setPosition(self.position);
try self.renderedGeo.addGeoLine(yOffset + self.position.y, 0);
var largestXOffset: f32 = 0;
for (self.string.?.*, 0..) |ch, i| {
if (i * 4 > self.mesh.maxVertexCount - 16) {
break;
}
if (!atlas.hasGlyph[ch]) {
try self.renderedGeo.addCharGeo(self.position.x + xOffset, stride, @intCast(i));
xOffset += stride;
continue;
}
if (ch == 0 or ch == '\r') {
continue;
}
if (ch == ' ' or (ch == '\n' and self.renderMode == .NoControl)) {
try self.renderedGeo.addCharGeo(self.position.x + xOffset, stride, @intCast(i));
xOffset += stride;
continue;
}
// newline if we see newline and we're in simple or rich mode.
if (ch == '\n' and (self.renderMode == .Simple or self.renderMode == .Rich)) {
try self.renderedGeo.addCharGeo(self.position.x + xOffset, stride, @intCast(i));
xOffset = 0;
yOffset += fontHeight * 1.2;
try self.renderedGeo.addGeoLine(yOffset + self.position.y, @intCast(i));
continue;
}
if (ch == ' ') {
try self.renderedGeo.addCharGeo(self.position.x + xOffset, stride, @intCast(i));
xOffset += stride;
continue;
}
const box = Vector2f.from(atlas.glyphBox1[ch]).fmul(ratio);
const metrics = Vector2f.from(atlas.glyphMetrics[ch]).fmul(ratio);
const baseMetrics = Vector2f.from(atlas.glyphMetrics[ch]);
const uv_tl = atlas.glyphCoordinates[ch][0];
xOffset += box.x;
//if (xOffset + box.x + metrics.x > self.boxSize.x) {
if (xOffset + box.x + metrics.x > self.boxSize.x) {
xOffset = 0;
yOffset += fontHeight * 1.2;
try self.renderedGeo.addGeoLine(yOffset + self.position.y, @intCast(i));
}
const color = self.color;
const topLeft = .{
// .x = self.position.x + xOffset + box.x,
// .y = yOffset + self.position.y + box.y + fontHeight,
.x = xOffset,
.y = yOffset + box.y + fontHeight,
};
const metric_size = .{ .x = metrics.x, .y = metrics.y, .z = 0 };
self.mesh.addQuad2D(
topLeft,
metric_size,
.{ .x = uv_tl.x, .y = uv_tl.y }, // uv topleft
.{
.x = baseMetrics.x / @as(f32, @floatFromInt(atlas.atlasSize.x)),
.y = baseMetrics.y / @as(f32, @floatFromInt(atlas.atlasSize.y)),
}, // uv size
.{ .r = color.r, .g = color.g, .b = color.b }, // color
);
// todo insert geo
//try self.renderedGeo.addCharGeo(self.position.x + xOffset, box.x + metrics.x, @intCast(i));
//xOffset += box.x + metrics.x;
try self.renderedGeo.addCharGeo(self.position.x + xOffset, metrics.x, @intCast(i));
xOffset += metrics.x;
if (xOffset > largestXOffset) {
largestXOffset = xOffset;
}
}
try self.renderedGeo.addCharGeo(self.position.x + xOffset, 200.0, @intCast(self.string.?.len));
self.renderedSize = .{
.x = largestXOffset,
.y = yOffset + fontHeight * 1.2,
};
self.renderedGeo.setBoundsX(self.position.x, self.position.x + self.renderedSize.x);
}
};
// list of texts to display
pub const TextRenderer = struct {
g: *graphics.NeonVkContext,
allocator: std.mem.Allocator,
backingAllocator: std.mem.Allocator,
arena: std.heap.ArenaAllocator,
displays: ArrayListU(*DisplayText) = .{},
smallDisplays: ArrayListU(*DisplayText) = .{},
fonts: AutoHashMapU(u32, *FontAtlasVk) = .{},
small_limit: u32,
papyrusCtx: *papyrus.Context,
pub fn init(backingAllocator: std.mem.Allocator, g: *graphics.NeonVkContext, papyrusCtx: *papyrus.Context) !*@This() {
var self = try backingAllocator.create(@This());
self.* = .{
.allocator = undefined,
.backingAllocator = backingAllocator,
.arena = std.heap.ArenaAllocator.init(backingAllocator),
.g = g,
.papyrusCtx = papyrusCtx,
.small_limit = 512,
};
self.allocator = self.arena.allocator();
var new = try self.allocator.create(FontAtlasVk);
new.* = try FontAtlasVk.init(self.allocator, self.g);
new.isDefault = true;
new.atlas = papyrusCtx.defaultFont.atlas; // use default font instead of loading a font from text file
var defaultName = core.MakeName("default");
try new.prepareFont(defaultName);
try self.fonts.put(self.allocator, defaultName.handle(), new);
self.papyrusCtx.defaultFont.atlas.rendererHash = defaultName.handle();
var newMono = try self.allocator.create(FontAtlasVk);
newMono.* = try FontAtlasVk.init(self.allocator, self.g);
newMono.isDefault = true;
newMono.atlas = papyrusCtx.defaultMonoFont.atlas;
var monoName = core.MakeName("monospace");
try newMono.prepareFont(monoName);
try self.fonts.put(self.allocator, monoName.handle(), newMono);
self.papyrusCtx.defaultMonoFont.atlas.rendererHash = monoName.handle();
{
var newbitmap = try self.allocator.create(FontAtlasVk);
newbitmap.* = try FontAtlasVk.init(self.allocator, self.g);
newbitmap.isDefault = true;
newbitmap.atlas = papyrusCtx.defaultBitmapFont.atlas;
var bitmapName = core.MakeName("bitmap");
try newbitmap.prepareFont(bitmapName);
try self.fonts.put(self.allocator, bitmapName.handle(), newbitmap);
self.papyrusCtx.defaultBitmapFont.setRendererHash(bitmapName.handle());
}
var k: u32 = 0;
// we can support up to 32 large text displays and 256 small displays
// displayText with default settings is for large renders. eg. pages. code editors, etc..
for (0..4) |i| {
_ = i;
const newDisplay = try self.addDisplayText(core.MakeName("default"), .{
.charLimit = 8192,
});
k += 1;
try self.displays.append(self.allocator, newDisplay);
}
for (0..64) |i| {
_ = i;
const newDisplay = try self.addDisplayText(core.MakeName("default"), .{
.charLimit = 512,
});
k += 1;
try self.smallDisplays.append(self.allocator, newDisplay);
}
return self;
}
pub fn addFont(self: *@This(), ttfPath: []const u8, _name: core.Name) !*FontAtlasVk {
var name = _name;
var new = try self.allocator.create(FontAtlasVk);
const textureName = try std.fmt.allocPrint(self.allocator, "texture.font.{s}", .{name.utf8()});
defer self.allocator.free(textureName);
new.* = try FontAtlasVk.init(
self.allocator,
self.g,
);
try new.loadFont(self.papyrusCtx, ttfPath);
try new.prepareFont(core.Name.fromUtf8(textureName));
new.atlas.rendererHash = name.handle();
try self.papyrusCtx.installFontAtlas(name.utf8(), new.atlas);
try self.fonts.put(self.allocator, name.handle(), new);
return new;
}
pub fn addDisplayText(self: *@This(), _fontName: core.Name, opts: anytype) !*DisplayText {
const new = try self.allocator.create(DisplayText);
var fontName = _fontName;
new.* = try DisplayText.init(
self.allocator,
self.fonts.get(fontName.handle()).?,
opts,
);
return new;
}
pub const TextFrameContext = struct {
allocated: u32 = 0,
allocated_small: u32 = 0,
};
pub const TextFrameAlloc =
struct { index: u32, small: bool };
pub fn startRendering(_: @This()) TextFrameContext {
return .{};
}
pub fn getNextSlot(self: *@This(), len: usize, frameContext: *TextFrameContext) TextFrameAlloc {
if (len >= self.small_limit) {
const rv: TextFrameAlloc = .{ .small = false, .index = frameContext.allocated };
frameContext.allocated += 1;
return rv;
}
const rv: TextFrameAlloc = .{ .small = true, .index = frameContext.allocated_small };
frameContext.allocated_small += 1;
return rv;
}
pub fn deinit(self: *@This(), backingAllocator: std.mem.Allocator) void {
for (self.displays.items) |display| {
display.deinit();
}
for (self.smallDisplays.items) |display| {
display.deinit();
}
self.arena.deinit();
backingAllocator.destroy(self);
}
};

View File

@ -1,46 +0,0 @@
const std = @import("std");
const core = @import("core");
const graphics = @import("graphics");
const memory = core.MemoryTracker;
pub const papyrus = @import("papyrus");
pub const HandlerError = papyrus.HandlerError;
pub const NodeHandle = papyrus.NodeHandle;
pub const LocText = papyrus.LocText;
pub const PressedType = papyrus.PressedType;
pub const PapyrusSystem = @import("PapyrusIntegration.zig");
var gPapyrus: *PapyrusSystem = undefined;
pub const Module: core.ModuleDescription = .{
.name = "ui",
.enabledByDefault = true,
};
pub fn getSystem() *PapyrusSystem {
return gPapyrus;
}
pub fn getContext() *papyrus.Context {
return gPapyrus.papyrusCtx;
}
pub fn start_module(comptime spec: anytype, args: anytype, allocator: std.mem.Allocator) !void {
_ = args;
_ = spec;
_ = allocator;
// no initialization
if (core.isUtility()) {
return;
}
gPapyrus = try core.gEngine.createObject(PapyrusSystem, .{ .can_tick = true });
try gPapyrus.setup(graphics.getContext());
core.engine_logs("ui start_module");
memory.MTPrintStatsDelta();
}
pub fn shutdown_module(allocator: std.mem.Allocator) void {
_ = allocator;
}

View File

@ -1,86 +0,0 @@
const std = @import("std");
// very tiny, not intended to build anything just to run tests linked with libc
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const mod = b.addModule("vkImgui", .{
.target = target,
.optimize = optimize,
.link_libc = true,
.root_source_file = b.path("src/vkImgui.zig"),
});
mod.addIncludePath(b.path("cimgui"));
mod.addIncludePath(b.path("cimplot"));
mod.addIncludePath(b.path("cimgui/imgui"));
mod.addIncludePath(b.path("cimgui/imgui/backends"));
const cimgui = b.addStaticLibrary(.{
.name = "cimgui",
.target = target,
.optimize = optimize,
});
cimgui.linkLibC();
if (target.result.abi != .msvc)
cimgui.linkLibCpp();
cimgui.addIncludePath(b.path("cimgui"));
cimgui.addIncludePath(b.path("cimplot"));
cimgui.addIncludePath(b.path("cimgui/imgui"));
cimgui.addIncludePath(b.path("cimgui/imgui/backends"));
cimgui.addCSourceFiles(.{
.root = b.path("cimgui/imgui"),
.files = &[_][]const u8{
"cimgui.cpp",
"cimgui_compat.cpp",
"imgui.cpp",
"imgui_demo.cpp",
"imgui_draw.cpp",
"imgui_tables.cpp",
"imgui_widgets.cpp",
"backends/imgui_impl_vulkan.cpp",
"backends/imgui_impl_glfw.cpp",
},
});
cimgui.addCSourceFiles(.{
.root = b.path("cimplot"),
.files = &[_][]const u8{
"cimplot.cpp",
"implot/implot.cpp",
"implot/implot_demo.cpp",
"implot/implot_items.cpp",
},
});
const depList = [_][]const u8{
"core",
"graphics",
"platform",
"vulkan",
};
for (depList) |depName| {
const dep = b.dependency(depName, .{ .target = target, .optimize = optimize });
const depMod = dep.module(depName);
mod.addImport(depName, depMod);
}
mod.linkLibrary(cimgui);
// I could've made cimgui a seperate lib,
// I can seperate it out later if needed.
const test_step = b.step("test", "run unit tests for ui");
const tests = b.addTest(.{
.target = target,
.optimize = optimize,
.root_source_file = b.path("tests/tests.zig"),
});
tests.root_module.addImport("vkImgui", mod);
const runArtifact = b.addRunArtifact(tests);
test_step.dependOn(&runArtifact.step);
b.installArtifact(tests);
}

View File

@ -1,13 +0,0 @@
.{
.name = "imgui",
.version = "0.0.0",
.dependencies = .{
.core = .{ .path = "../core" },
.graphics = .{ .path = "../graphics" },
.platform = .{ .path = "../platform" },
.vulkan = .{ .path = "../../lib/vulkan" },
},
.paths = .{
"",
},
}

File diff suppressed because it is too large Load Diff

View File

@ -1,594 +0,0 @@
/*************************************************************************
* GLFW 3.3 - www.glfw.org
* A library for OpenGL, window and input
*------------------------------------------------------------------------
* Copyright (c) 2002-2006 Marcus Geelnard
* Copyright (c) 2006-2018 Camilla Löwy <elmindreda@glfw.org>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would
* be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*
*************************************************************************/
#ifndef _glfw3_native_h_
#define _glfw3_native_h_
#ifdef __cplusplus
extern "C" {
#endif
/*************************************************************************
* Doxygen documentation
*************************************************************************/
/*! @file glfw3native.h
* @brief The header of the native access functions.
*
* This is the header file of the native access functions. See @ref native for
* more information.
*/
/*! @defgroup native Native access
* @brief Functions related to accessing native handles.
*
* **By using the native access functions you assert that you know what you're
* doing and how to fix problems caused by using them. If you don't, you
* shouldn't be using them.**
*
* Before the inclusion of @ref glfw3native.h, you may define zero or more
* window system API macro and zero or more context creation API macros.
*
* The chosen backends must match those the library was compiled for. Failure
* to do this will cause a link-time error.
*
* The available window API macros are:
* * `GLFW_EXPOSE_NATIVE_WIN32`
* * `GLFW_EXPOSE_NATIVE_COCOA`
* * `GLFW_EXPOSE_NATIVE_X11`
* * `GLFW_EXPOSE_NATIVE_WAYLAND`
*
* The available context API macros are:
* * `GLFW_EXPOSE_NATIVE_WGL`
* * `GLFW_EXPOSE_NATIVE_NSGL`
* * `GLFW_EXPOSE_NATIVE_GLX`
* * `GLFW_EXPOSE_NATIVE_EGL`
* * `GLFW_EXPOSE_NATIVE_OSMESA`
*
* These macros select which of the native access functions that are declared
* and which platform-specific headers to include. It is then up your (by
* definition platform-specific) code to handle which of these should be
* defined.
*/
/*************************************************************************
* System headers and types
*************************************************************************/
#if defined(GLFW_EXPOSE_NATIVE_WIN32) || defined(GLFW_EXPOSE_NATIVE_WGL)
// This is a workaround for the fact that glfw3.h needs to export APIENTRY (for
// example to allow applications to correctly declare a GL_KHR_debug callback)
// but windows.h assumes no one will define APIENTRY before it does
#if defined(GLFW_APIENTRY_DEFINED)
#undef APIENTRY
#undef GLFW_APIENTRY_DEFINED
#endif
#include <windows.h>
#elif defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL)
#if defined(__OBJC__)
#import <Cocoa/Cocoa.h>
#else
#include <ApplicationServices/ApplicationServices.h>
typedef void* id;
#endif
#elif defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX)
#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>
#elif defined(GLFW_EXPOSE_NATIVE_WAYLAND)
#include <wayland-client.h>
#endif
#if defined(GLFW_EXPOSE_NATIVE_WGL)
/* WGL is declared by windows.h */
#endif
#if defined(GLFW_EXPOSE_NATIVE_NSGL)
/* NSGL is declared by Cocoa.h */
#endif
#if defined(GLFW_EXPOSE_NATIVE_GLX)
#include <GL/glx.h>
#endif
#if defined(GLFW_EXPOSE_NATIVE_EGL)
#include <EGL/egl.h>
#endif
#if defined(GLFW_EXPOSE_NATIVE_OSMESA)
#include <GL/osmesa.h>
#endif
/*************************************************************************
* Functions
*************************************************************************/
#if defined(GLFW_EXPOSE_NATIVE_WIN32)
/*! @brief Returns the adapter device name of the specified monitor.
*
* @return The UTF-8 encoded adapter device name (for example `\\.\DISPLAY1`)
* of the specified monitor, or `NULL` if an [error](@ref error_handling)
* occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* monitor);
/*! @brief Returns the display device name of the specified monitor.
*
* @return The UTF-8 encoded display device name (for example
* `\\.\DISPLAY1\Monitor0`) of the specified monitor, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* monitor);
/*! @brief Returns the `HWND` of the specified window.
*
* @return The `HWND` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @remark The `HDC` associated with the window can be queried with the
* [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc)
* function.
* @code
* HDC dc = GetDC(glfwGetWin32Window(window));
* @endcode
* This DC is private and does not need to be released.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_WGL)
/*! @brief Returns the `HGLRC` of the specified window.
*
* @return The `HGLRC` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
* GLFW_NOT_INITIALIZED.
*
* @remark The `HDC` associated with the window can be queried with the
* [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc)
* function.
* @code
* HDC dc = GetDC(glfwGetWin32Window(window));
* @endcode
* This DC is private and does not need to be released.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_COCOA)
/*! @brief Returns the `CGDirectDisplayID` of the specified monitor.
*
* @return The `CGDirectDisplayID` of the specified monitor, or
* `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor);
/*! @brief Returns the `NSWindow` of the specified window.
*
* @return The `NSWindow` of the specified window, or `nil` if an
* [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_NSGL)
/*! @brief Returns the `NSOpenGLContext` of the specified window.
*
* @return The `NSOpenGLContext` of the specified window, or `nil` if an
* [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
* GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI id glfwGetNSGLContext(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_X11)
/*! @brief Returns the `Display` used by GLFW.
*
* @return The `Display` used by GLFW, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI Display* glfwGetX11Display(void);
/*! @brief Returns the `RRCrtc` of the specified monitor.
*
* @return The `RRCrtc` of the specified monitor, or `None` if an
* [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor);
/*! @brief Returns the `RROutput` of the specified monitor.
*
* @return The `RROutput` of the specified monitor, or `None` if an
* [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.1.
*
* @ingroup native
*/
GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor);
/*! @brief Returns the `Window` of the specified window.
*
* @return The `Window` of the specified window, or `None` if an
* [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI Window glfwGetX11Window(GLFWwindow* window);
/*! @brief Sets the current primary selection to the specified string.
*
* @param[in] string A UTF-8 encoded string.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @pointer_lifetime The specified string is copied before this function
* returns.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref clipboard
* @sa glfwGetX11SelectionString
* @sa glfwSetClipboardString
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI void glfwSetX11SelectionString(const char* string);
/*! @brief Returns the contents of the current primary selection as a string.
*
* If the selection is empty or if its contents cannot be converted, `NULL`
* is returned and a @ref GLFW_FORMAT_UNAVAILABLE error is generated.
*
* @return The contents of the selection as a UTF-8 encoded string, or `NULL`
* if an [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref
* GLFW_PLATFORM_ERROR.
*
* @pointer_lifetime The returned string is allocated and freed by GLFW. You
* should not free it yourself. It is valid until the next call to @ref
* glfwGetX11SelectionString or @ref glfwSetX11SelectionString, or until the
* library is terminated.
*
* @thread_safety This function must only be called from the main thread.
*
* @sa @ref clipboard
* @sa glfwSetX11SelectionString
* @sa glfwGetClipboardString
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI const char* glfwGetX11SelectionString(void);
#endif
#if defined(GLFW_EXPOSE_NATIVE_GLX)
/*! @brief Returns the `GLXContext` of the specified window.
*
* @return The `GLXContext` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
* GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window);
/*! @brief Returns the `GLXWindow` of the specified window.
*
* @return The `GLXWindow` of the specified window, or `None` if an
* [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
* GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_WAYLAND)
/*! @brief Returns the `struct wl_display*` used by GLFW.
*
* @return The `struct wl_display*` used by GLFW, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI struct wl_display* glfwGetWaylandDisplay(void);
/*! @brief Returns the `struct wl_output*` of the specified monitor.
*
* @return The `struct wl_output*` of the specified monitor, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor);
/*! @brief Returns the main `struct wl_surface*` of the specified window.
*
* @return The main `struct wl_surface*` of the specified window, or `NULL` if
* an [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.2.
*
* @ingroup native
*/
GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_EGL)
/*! @brief Returns the `EGLDisplay` used by GLFW.
*
* @return The `EGLDisplay` used by GLFW, or `EGL_NO_DISPLAY` if an
* [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI EGLDisplay glfwGetEGLDisplay(void);
/*! @brief Returns the `EGLContext` of the specified window.
*
* @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an
* [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
* GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window);
/*! @brief Returns the `EGLSurface` of the specified window.
*
* @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an
* [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
* GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.0.
*
* @ingroup native
*/
GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window);
#endif
#if defined(GLFW_EXPOSE_NATIVE_OSMESA)
/*! @brief Retrieves the color buffer associated with the specified window.
*
* @param[in] window The window whose color buffer to retrieve.
* @param[out] width Where to store the width of the color buffer, or `NULL`.
* @param[out] height Where to store the height of the color buffer, or `NULL`.
* @param[out] format Where to store the OSMesa pixel format of the color
* buffer, or `NULL`.
* @param[out] buffer Where to store the address of the color buffer, or
* `NULL`.
* @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an
* [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
* GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, int* height, int* format, void** buffer);
/*! @brief Retrieves the depth buffer associated with the specified window.
*
* @param[in] window The window whose depth buffer to retrieve.
* @param[out] width Where to store the width of the depth buffer, or `NULL`.
* @param[out] height Where to store the height of the depth buffer, or `NULL`.
* @param[out] bytesPerValue Where to store the number of bytes per depth
* buffer element, or `NULL`.
* @param[out] buffer Where to store the address of the depth buffer, or
* `NULL`.
* @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an
* [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
* GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, int* height, int* bytesPerValue, void** buffer);
/*! @brief Returns the `OSMesaContext` of the specified window.
*
* @return The `OSMesaContext` of the specified window, or `NULL` if an
* [error](@ref error_handling) occurred.
*
* @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref
* GLFW_NOT_INITIALIZED.
*
* @thread_safety This function may be called from any thread. Access is not
* synchronized.
*
* @since Added in version 3.3.
*
* @ingroup native
*/
GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* window);
#endif
#ifdef __cplusplus
}
#endif
#endif /* _glfw3_native_h_ */

View File

@ -1,581 +0,0 @@
// dear imgui: Renderer + Platform Backend for Allegro 5
// (Info: Allegro 5 is a cross-platform general purpose library for handling windows, inputs, graphics, etc.)
// Implemented features:
// [X] Renderer: User texture binding. Use 'ALLEGRO_BITMAP*' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy ALLEGRO_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
// [X] Platform: Clipboard support (from Allegro 5.1.12)
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// Issues:
// [ ] Renderer: The renderer is suboptimal as we need to unindex our buffers and convert vertices manually.
// [ ] Platform: Missing gamepad support.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago)with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion.
// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+).
// 2022-01-17: Inputs: always calling io.AddKeyModsEvent() next and before key event (not in NewFrame) to fix input queue with very low framerates.
// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range.
// 2021-12-08: Renderer: Fixed mishandling of the the ImDrawCmd::IdxOffset field! This is an old bug but it never had an effect until some internal rendering changes in 1.86.
// 2021-08-17: Calling io.AddFocusEvent() on ALLEGRO_EVENT_DISPLAY_SWITCH_OUT/ALLEGRO_EVENT_DISPLAY_SWITCH_IN events.
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
// 2021-05-19: Renderer: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
// 2021-02-18: Change blending equation to preserve alpha in output buffer.
// 2020-08-10: Inputs: Fixed horizontal mouse wheel direction.
// 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor.
// 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter.
// 2019-05-11: Inputs: Don't filter character value from ALLEGRO_EVENT_KEY_CHAR before calling AddInputCharacter().
// 2019-04-30: Renderer: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2018-11-30: Platform: Added touchscreen support.
// 2018-11-30: Misc: Setting up io.BackendPlatformName/io.BackendRendererName so they can be displayed in the About Window.
// 2018-06-13: Platform: Added clipboard support (from Allegro 5.1.12).
// 2018-06-13: Renderer: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-06-13: Renderer: Backup/restore transform and clipping rectangle.
// 2018-06-11: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag.
// 2018-04-18: Misc: Renamed file from imgui_impl_a5.cpp to imgui_impl_allegro5.cpp.
// 2018-04-18: Misc: Added support for 32-bit vertex indices to avoid conversion at runtime. Added imconfig_allegro5.h to enforce 32-bit indices when included from imgui.h.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplAllegro5_RenderDrawData() in the .h file so you can call it yourself.
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
#include <stdint.h> // uint64_t
#include <cstring> // memcpy
#include "imgui.h"
#include "imgui_impl_allegro5.h"
// Allegro
#include <allegro5/allegro.h>
#include <allegro5/allegro_primitives.h>
#ifdef _WIN32
#include <allegro5/allegro_windows.h>
#endif
#define ALLEGRO_HAS_CLIPBOARD (ALLEGRO_VERSION_INT >= ((5 << 24) | (1 << 16) | (12 << 8))) // Clipboard only supported from Allegro 5.1.12
// Visual Studio warnings
#ifdef _MSC_VER
#pragma warning (disable: 4127) // condition expression is constant
#endif
// Allegro Data
struct ImGui_ImplAllegro5_Data
{
ALLEGRO_DISPLAY* Display;
ALLEGRO_BITMAP* Texture;
double Time;
ALLEGRO_MOUSE_CURSOR* MouseCursorInvisible;
ALLEGRO_VERTEX_DECL* VertexDecl;
char* ClipboardTextData;
ImGui_ImplAllegro5_Data() { memset((void*)this, 0, sizeof(*this)); }
};
// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
// FIXME: multi-context support is not well tested and probably dysfunctional in this backend.
static ImGui_ImplAllegro5_Data* ImGui_ImplAllegro5_GetBackendData() { return ImGui::GetCurrentContext() ? (ImGui_ImplAllegro5_Data*)ImGui::GetIO().BackendPlatformUserData : NULL; }
struct ImDrawVertAllegro
{
ImVec2 pos;
ImVec2 uv;
ALLEGRO_COLOR col;
};
static void ImGui_ImplAllegro5_SetupRenderState(ImDrawData* draw_data)
{
// Setup blending
al_set_separate_blender(ALLEGRO_ADD, ALLEGRO_ALPHA, ALLEGRO_INVERSE_ALPHA, ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_INVERSE_ALPHA);
// Setup orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right).
{
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
ALLEGRO_TRANSFORM transform;
al_identity_transform(&transform);
al_use_transform(&transform);
al_orthographic_transform(&transform, L, T, 1.0f, R, B, -1.0f);
al_use_projection_transform(&transform);
}
}
// Render function.
void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
return;
// Backup Allegro state that will be modified
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
ALLEGRO_TRANSFORM last_transform = *al_get_current_transform();
ALLEGRO_TRANSFORM last_projection_transform = *al_get_current_projection_transform();
int last_clip_x, last_clip_y, last_clip_w, last_clip_h;
al_get_clipping_rectangle(&last_clip_x, &last_clip_y, &last_clip_w, &last_clip_h);
int last_blender_op, last_blender_src, last_blender_dst;
al_get_blender(&last_blender_op, &last_blender_src, &last_blender_dst);
// Setup desired render state
ImGui_ImplAllegro5_SetupRenderState(draw_data);
// Render command lists
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
// Allegro's implementation of al_draw_indexed_prim() for DX9 is completely broken. Unindex our buffers ourselves.
// FIXME-OPT: Unfortunately Allegro doesn't support 32-bit packed colors so we have to convert them to 4 float as well..
static ImVector<ImDrawVertAllegro> vertices;
vertices.resize(cmd_list->IdxBuffer.Size);
for (int i = 0; i < cmd_list->IdxBuffer.Size; i++)
{
const ImDrawVert* src_v = &cmd_list->VtxBuffer[cmd_list->IdxBuffer[i]];
ImDrawVertAllegro* dst_v = &vertices[i];
dst_v->pos = src_v->pos;
dst_v->uv = src_v->uv;
unsigned char* c = (unsigned char*)&src_v->col;
dst_v->col = al_map_rgba(c[0], c[1], c[2], c[3]);
}
const int* indices = NULL;
if (sizeof(ImDrawIdx) == 2)
{
// FIXME-OPT: Unfortunately Allegro doesn't support 16-bit indices.. You can '#define ImDrawIdx int' in imconfig.h to request Dear ImGui to output 32-bit indices.
// Otherwise, we convert them from 16-bit to 32-bit at runtime here, which works perfectly but is a little wasteful.
static ImVector<int> indices_converted;
indices_converted.resize(cmd_list->IdxBuffer.Size);
for (int i = 0; i < cmd_list->IdxBuffer.Size; ++i)
indices_converted[i] = (int)cmd_list->IdxBuffer.Data[i];
indices = indices_converted.Data;
}
else if (sizeof(ImDrawIdx) == 4)
{
indices = (const int*)cmd_list->IdxBuffer.Data;
}
// Render command lists
ImVec2 clip_off = draw_data->DisplayPos;
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplAllegro5_SetupRenderState(draw_data);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);
ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
continue;
// Apply scissor/clipping rectangle, Draw
ALLEGRO_BITMAP* texture = (ALLEGRO_BITMAP*)pcmd->GetTexID();
al_set_clipping_rectangle(clip_min.x, clip_min.y, clip_max.x - clip_min.x, clip_max.y - clip_min.y);
al_draw_prim(&vertices[0], bd->VertexDecl, texture, pcmd->IdxOffset, pcmd->IdxOffset + pcmd->ElemCount, ALLEGRO_PRIM_TRIANGLE_LIST);
}
}
}
// Restore modified Allegro state
al_set_blender(last_blender_op, last_blender_src, last_blender_dst);
al_set_clipping_rectangle(last_clip_x, last_clip_y, last_clip_w, last_clip_h);
al_use_transform(&last_transform);
al_use_projection_transform(&last_projection_transform);
}
bool ImGui_ImplAllegro5_CreateDeviceObjects()
{
// Build texture atlas
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
// Create texture
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
int flags = al_get_new_bitmap_flags();
int fmt = al_get_new_bitmap_format();
al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP | ALLEGRO_MIN_LINEAR | ALLEGRO_MAG_LINEAR);
al_set_new_bitmap_format(ALLEGRO_PIXEL_FORMAT_ABGR_8888_LE);
ALLEGRO_BITMAP* img = al_create_bitmap(width, height);
al_set_new_bitmap_flags(flags);
al_set_new_bitmap_format(fmt);
if (!img)
return false;
ALLEGRO_LOCKED_REGION* locked_img = al_lock_bitmap(img, al_get_bitmap_format(img), ALLEGRO_LOCK_WRITEONLY);
if (!locked_img)
{
al_destroy_bitmap(img);
return false;
}
memcpy(locked_img->data, pixels, sizeof(int) * width * height);
al_unlock_bitmap(img);
// Convert software texture to hardware texture.
ALLEGRO_BITMAP* cloned_img = al_clone_bitmap(img);
al_destroy_bitmap(img);
if (!cloned_img)
return false;
// Store our identifier
io.Fonts->SetTexID((ImTextureID)(intptr_t)cloned_img);
bd->Texture = cloned_img;
// Create an invisible mouse cursor
// Because al_hide_mouse_cursor() seems to mess up with the actual inputs..
ALLEGRO_BITMAP* mouse_cursor = al_create_bitmap(8, 8);
bd->MouseCursorInvisible = al_create_mouse_cursor(mouse_cursor, 0, 0);
al_destroy_bitmap(mouse_cursor);
return true;
}
void ImGui_ImplAllegro5_InvalidateDeviceObjects()
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
if (bd->Texture)
{
io.Fonts->SetTexID(NULL);
al_destroy_bitmap(bd->Texture);
bd->Texture = NULL;
}
if (bd->MouseCursorInvisible)
{
al_destroy_mouse_cursor(bd->MouseCursorInvisible);
bd->MouseCursorInvisible = NULL;
}
}
#if ALLEGRO_HAS_CLIPBOARD
static const char* ImGui_ImplAllegro5_GetClipboardText(void*)
{
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
if (bd->ClipboardTextData)
al_free(bd->ClipboardTextData);
bd->ClipboardTextData = al_get_clipboard_text(bd->Display);
return bd->ClipboardTextData;
}
static void ImGui_ImplAllegro5_SetClipboardText(void*, const char* text)
{
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
al_set_clipboard_text(bd->Display, text);
}
#endif
static ImGuiKey ImGui_ImplAllegro5_KeyCodeToImGuiKey(int key_code)
{
switch (key_code)
{
case ALLEGRO_KEY_TAB: return ImGuiKey_Tab;
case ALLEGRO_KEY_LEFT: return ImGuiKey_LeftArrow;
case ALLEGRO_KEY_RIGHT: return ImGuiKey_RightArrow;
case ALLEGRO_KEY_UP: return ImGuiKey_UpArrow;
case ALLEGRO_KEY_DOWN: return ImGuiKey_DownArrow;
case ALLEGRO_KEY_PGUP: return ImGuiKey_PageUp;
case ALLEGRO_KEY_PGDN: return ImGuiKey_PageDown;
case ALLEGRO_KEY_HOME: return ImGuiKey_Home;
case ALLEGRO_KEY_END: return ImGuiKey_End;
case ALLEGRO_KEY_INSERT: return ImGuiKey_Insert;
case ALLEGRO_KEY_DELETE: return ImGuiKey_Delete;
case ALLEGRO_KEY_BACKSPACE: return ImGuiKey_Backspace;
case ALLEGRO_KEY_SPACE: return ImGuiKey_Space;
case ALLEGRO_KEY_ENTER: return ImGuiKey_Enter;
case ALLEGRO_KEY_ESCAPE: return ImGuiKey_Escape;
case ALLEGRO_KEY_QUOTE: return ImGuiKey_Apostrophe;
case ALLEGRO_KEY_COMMA: return ImGuiKey_Comma;
case ALLEGRO_KEY_MINUS: return ImGuiKey_Minus;
case ALLEGRO_KEY_FULLSTOP: return ImGuiKey_Period;
case ALLEGRO_KEY_SLASH: return ImGuiKey_Slash;
case ALLEGRO_KEY_SEMICOLON: return ImGuiKey_Semicolon;
case ALLEGRO_KEY_EQUALS: return ImGuiKey_Equal;
case ALLEGRO_KEY_OPENBRACE: return ImGuiKey_LeftBracket;
case ALLEGRO_KEY_BACKSLASH: return ImGuiKey_Backslash;
case ALLEGRO_KEY_CLOSEBRACE: return ImGuiKey_RightBracket;
case ALLEGRO_KEY_TILDE: return ImGuiKey_GraveAccent;
case ALLEGRO_KEY_CAPSLOCK: return ImGuiKey_CapsLock;
case ALLEGRO_KEY_SCROLLLOCK: return ImGuiKey_ScrollLock;
case ALLEGRO_KEY_NUMLOCK: return ImGuiKey_NumLock;
case ALLEGRO_KEY_PRINTSCREEN: return ImGuiKey_PrintScreen;
case ALLEGRO_KEY_PAUSE: return ImGuiKey_Pause;
case ALLEGRO_KEY_PAD_0: return ImGuiKey_Keypad0;
case ALLEGRO_KEY_PAD_1: return ImGuiKey_Keypad1;
case ALLEGRO_KEY_PAD_2: return ImGuiKey_Keypad2;
case ALLEGRO_KEY_PAD_3: return ImGuiKey_Keypad3;
case ALLEGRO_KEY_PAD_4: return ImGuiKey_Keypad4;
case ALLEGRO_KEY_PAD_5: return ImGuiKey_Keypad5;
case ALLEGRO_KEY_PAD_6: return ImGuiKey_Keypad6;
case ALLEGRO_KEY_PAD_7: return ImGuiKey_Keypad7;
case ALLEGRO_KEY_PAD_8: return ImGuiKey_Keypad8;
case ALLEGRO_KEY_PAD_9: return ImGuiKey_Keypad9;
case ALLEGRO_KEY_PAD_DELETE: return ImGuiKey_KeypadDecimal;
case ALLEGRO_KEY_PAD_SLASH: return ImGuiKey_KeypadDivide;
case ALLEGRO_KEY_PAD_ASTERISK: return ImGuiKey_KeypadMultiply;
case ALLEGRO_KEY_PAD_MINUS: return ImGuiKey_KeypadSubtract;
case ALLEGRO_KEY_PAD_PLUS: return ImGuiKey_KeypadAdd;
case ALLEGRO_KEY_PAD_ENTER: return ImGuiKey_KeypadEnter;
case ALLEGRO_KEY_PAD_EQUALS: return ImGuiKey_KeypadEqual;
case ALLEGRO_KEY_LCTRL: return ImGuiKey_LeftCtrl;
case ALLEGRO_KEY_LSHIFT: return ImGuiKey_LeftShift;
case ALLEGRO_KEY_ALT: return ImGuiKey_LeftAlt;
case ALLEGRO_KEY_LWIN: return ImGuiKey_LeftSuper;
case ALLEGRO_KEY_RCTRL: return ImGuiKey_RightCtrl;
case ALLEGRO_KEY_RSHIFT: return ImGuiKey_RightShift;
case ALLEGRO_KEY_ALTGR: return ImGuiKey_RightAlt;
case ALLEGRO_KEY_RWIN: return ImGuiKey_RightSuper;
case ALLEGRO_KEY_MENU: return ImGuiKey_Menu;
case ALLEGRO_KEY_0: return ImGuiKey_0;
case ALLEGRO_KEY_1: return ImGuiKey_1;
case ALLEGRO_KEY_2: return ImGuiKey_2;
case ALLEGRO_KEY_3: return ImGuiKey_3;
case ALLEGRO_KEY_4: return ImGuiKey_4;
case ALLEGRO_KEY_5: return ImGuiKey_5;
case ALLEGRO_KEY_6: return ImGuiKey_6;
case ALLEGRO_KEY_7: return ImGuiKey_7;
case ALLEGRO_KEY_8: return ImGuiKey_8;
case ALLEGRO_KEY_9: return ImGuiKey_9;
case ALLEGRO_KEY_A: return ImGuiKey_A;
case ALLEGRO_KEY_B: return ImGuiKey_B;
case ALLEGRO_KEY_C: return ImGuiKey_C;
case ALLEGRO_KEY_D: return ImGuiKey_D;
case ALLEGRO_KEY_E: return ImGuiKey_E;
case ALLEGRO_KEY_F: return ImGuiKey_F;
case ALLEGRO_KEY_G: return ImGuiKey_G;
case ALLEGRO_KEY_H: return ImGuiKey_H;
case ALLEGRO_KEY_I: return ImGuiKey_I;
case ALLEGRO_KEY_J: return ImGuiKey_J;
case ALLEGRO_KEY_K: return ImGuiKey_K;
case ALLEGRO_KEY_L: return ImGuiKey_L;
case ALLEGRO_KEY_M: return ImGuiKey_M;
case ALLEGRO_KEY_N: return ImGuiKey_N;
case ALLEGRO_KEY_O: return ImGuiKey_O;
case ALLEGRO_KEY_P: return ImGuiKey_P;
case ALLEGRO_KEY_Q: return ImGuiKey_Q;
case ALLEGRO_KEY_R: return ImGuiKey_R;
case ALLEGRO_KEY_S: return ImGuiKey_S;
case ALLEGRO_KEY_T: return ImGuiKey_T;
case ALLEGRO_KEY_U: return ImGuiKey_U;
case ALLEGRO_KEY_V: return ImGuiKey_V;
case ALLEGRO_KEY_W: return ImGuiKey_W;
case ALLEGRO_KEY_X: return ImGuiKey_X;
case ALLEGRO_KEY_Y: return ImGuiKey_Y;
case ALLEGRO_KEY_Z: return ImGuiKey_Z;
case ALLEGRO_KEY_F1: return ImGuiKey_F1;
case ALLEGRO_KEY_F2: return ImGuiKey_F2;
case ALLEGRO_KEY_F3: return ImGuiKey_F3;
case ALLEGRO_KEY_F4: return ImGuiKey_F4;
case ALLEGRO_KEY_F5: return ImGuiKey_F5;
case ALLEGRO_KEY_F6: return ImGuiKey_F6;
case ALLEGRO_KEY_F7: return ImGuiKey_F7;
case ALLEGRO_KEY_F8: return ImGuiKey_F8;
case ALLEGRO_KEY_F9: return ImGuiKey_F9;
case ALLEGRO_KEY_F10: return ImGuiKey_F10;
case ALLEGRO_KEY_F11: return ImGuiKey_F11;
case ALLEGRO_KEY_F12: return ImGuiKey_F12;
default: return ImGuiKey_None;
}
}
bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display)
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.BackendPlatformUserData == NULL && "Already initialized a platform backend!");
// Setup backend capabilities flags
ImGui_ImplAllegro5_Data* bd = IM_NEW(ImGui_ImplAllegro5_Data)();
io.BackendPlatformUserData = (void*)bd;
io.BackendPlatformName = io.BackendRendererName = "imgui_impl_allegro5";
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
bd->Display = display;
// Create custom vertex declaration.
// Unfortunately Allegro doesn't support 32-bit packed colors so we have to convert them to 4 floats.
// We still use a custom declaration to use 'ALLEGRO_PRIM_TEX_COORD' instead of 'ALLEGRO_PRIM_TEX_COORD_PIXEL' else we can't do a reliable conversion.
ALLEGRO_VERTEX_ELEMENT elems[] =
{
{ ALLEGRO_PRIM_POSITION, ALLEGRO_PRIM_FLOAT_2, IM_OFFSETOF(ImDrawVertAllegro, pos) },
{ ALLEGRO_PRIM_TEX_COORD, ALLEGRO_PRIM_FLOAT_2, IM_OFFSETOF(ImDrawVertAllegro, uv) },
{ ALLEGRO_PRIM_COLOR_ATTR, 0, IM_OFFSETOF(ImDrawVertAllegro, col) },
{ 0, 0, 0 }
};
bd->VertexDecl = al_create_vertex_decl(elems, sizeof(ImDrawVertAllegro));
#if ALLEGRO_HAS_CLIPBOARD
io.SetClipboardTextFn = ImGui_ImplAllegro5_SetClipboardText;
io.GetClipboardTextFn = ImGui_ImplAllegro5_GetClipboardText;
io.ClipboardUserData = NULL;
#endif
return true;
}
void ImGui_ImplAllegro5_Shutdown()
{
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
IM_ASSERT(bd != NULL && "No platform backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplAllegro5_InvalidateDeviceObjects();
if (bd->VertexDecl)
al_destroy_vertex_decl(bd->VertexDecl);
if (bd->ClipboardTextData)
al_free(bd->ClipboardTextData);
io.BackendPlatformUserData = NULL;
io.BackendPlatformName = io.BackendRendererName = NULL;
IM_DELETE(bd);
}
// ev->keyboard.modifiers seems always zero so using that...
static void ImGui_ImplAllegro5_UpdateKeyModifiers()
{
ImGuiIO& io = ImGui::GetIO();
ALLEGRO_KEYBOARD_STATE keys;
al_get_keyboard_state(&keys);
io.AddKeyEvent(ImGuiKey_ModCtrl, al_key_down(&keys, ALLEGRO_KEY_LCTRL) || al_key_down(&keys, ALLEGRO_KEY_RCTRL));
io.AddKeyEvent(ImGuiKey_ModShift, al_key_down(&keys, ALLEGRO_KEY_LSHIFT) || al_key_down(&keys, ALLEGRO_KEY_RSHIFT));
io.AddKeyEvent(ImGuiKey_ModAlt, al_key_down(&keys, ALLEGRO_KEY_ALT) || al_key_down(&keys, ALLEGRO_KEY_ALTGR));
io.AddKeyEvent(ImGuiKey_ModSuper, al_key_down(&keys, ALLEGRO_KEY_LWIN) || al_key_down(&keys, ALLEGRO_KEY_RWIN));
}
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
bool ImGui_ImplAllegro5_ProcessEvent(ALLEGRO_EVENT* ev)
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
switch (ev->type)
{
case ALLEGRO_EVENT_MOUSE_AXES:
if (ev->mouse.display == bd->Display)
{
io.AddMousePosEvent(ev->mouse.x, ev->mouse.y);
io.AddMouseWheelEvent(-ev->mouse.dw, ev->mouse.dz);
}
return true;
case ALLEGRO_EVENT_MOUSE_BUTTON_DOWN:
case ALLEGRO_EVENT_MOUSE_BUTTON_UP:
if (ev->mouse.display == bd->Display && ev->mouse.button > 0 && ev->mouse.button <= 5)
io.AddMouseButtonEvent(ev->mouse.button - 1, ev->type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN);
return true;
case ALLEGRO_EVENT_TOUCH_MOVE:
if (ev->touch.display == bd->Display)
io.AddMousePosEvent(ev->touch.x, ev->touch.y);
return true;
case ALLEGRO_EVENT_TOUCH_BEGIN:
case ALLEGRO_EVENT_TOUCH_END:
case ALLEGRO_EVENT_TOUCH_CANCEL:
if (ev->touch.display == bd->Display && ev->touch.primary)
io.AddMouseButtonEvent(0, ev->type == ALLEGRO_EVENT_TOUCH_BEGIN);
return true;
case ALLEGRO_EVENT_MOUSE_LEAVE_DISPLAY:
if (ev->mouse.display == bd->Display)
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
return true;
case ALLEGRO_EVENT_KEY_CHAR:
if (ev->keyboard.display == bd->Display)
if (ev->keyboard.unichar != 0)
io.AddInputCharacter((unsigned int)ev->keyboard.unichar);
return true;
case ALLEGRO_EVENT_KEY_DOWN:
case ALLEGRO_EVENT_KEY_UP:
if (ev->keyboard.display == bd->Display)
{
ImGui_ImplAllegro5_UpdateKeyModifiers();
ImGuiKey key = ImGui_ImplAllegro5_KeyCodeToImGuiKey(ev->keyboard.keycode);
io.AddKeyEvent(key, (ev->type == ALLEGRO_EVENT_KEY_DOWN));
io.SetKeyEventNativeData(key, ev->keyboard.keycode, -1); // To support legacy indexing (<1.87 user code)
}
return true;
case ALLEGRO_EVENT_DISPLAY_SWITCH_OUT:
if (ev->display.source == bd->Display)
io.AddFocusEvent(false);
return true;
case ALLEGRO_EVENT_DISPLAY_SWITCH_IN:
if (ev->display.source == bd->Display)
{
io.AddFocusEvent(true);
#if defined(ALLEGRO_UNSTABLE)
al_clear_keyboard_state(bd->Display);
#endif
}
return true;
}
return false;
}
static void ImGui_ImplAllegro5_UpdateMouseCursor()
{
ImGuiIO& io = ImGui::GetIO();
if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
return;
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None)
{
// Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
al_set_mouse_cursor(bd->Display, bd->MouseCursorInvisible);
}
else
{
ALLEGRO_SYSTEM_MOUSE_CURSOR cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_DEFAULT;
switch (imgui_cursor)
{
case ImGuiMouseCursor_TextInput: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_EDIT; break;
case ImGuiMouseCursor_ResizeAll: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_MOVE; break;
case ImGuiMouseCursor_ResizeNS: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_RESIZE_N; break;
case ImGuiMouseCursor_ResizeEW: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_RESIZE_E; break;
case ImGuiMouseCursor_ResizeNESW: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_RESIZE_NE; break;
case ImGuiMouseCursor_ResizeNWSE: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_RESIZE_NW; break;
case ImGuiMouseCursor_NotAllowed: cursor_id = ALLEGRO_SYSTEM_MOUSE_CURSOR_UNAVAILABLE; break;
}
al_set_system_mouse_cursor(bd->Display, cursor_id);
}
}
void ImGui_ImplAllegro5_NewFrame()
{
ImGui_ImplAllegro5_Data* bd = ImGui_ImplAllegro5_GetBackendData();
IM_ASSERT(bd != NULL && "Did you call ImGui_ImplAllegro5_Init()?");
if (!bd->Texture)
ImGui_ImplAllegro5_CreateDeviceObjects();
ImGuiIO& io = ImGui::GetIO();
// Setup display size (every frame to accommodate for window resizing)
int w, h;
w = al_get_display_width(bd->Display);
h = al_get_display_height(bd->Display);
io.DisplaySize = ImVec2((float)w, (float)h);
// Setup time step
double current_time = al_get_time();
io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f);
bd->Time = current_time;
// Setup mouse cursor shape
ImGui_ImplAllegro5_UpdateMouseCursor();
}

View File

@ -1,32 +0,0 @@
// dear imgui: Renderer + Platform Backend for Allegro 5
// (Info: Allegro 5 is a cross-platform general purpose library for handling windows, inputs, graphics, etc.)
// Implemented features:
// [X] Renderer: User texture binding. Use 'ALLEGRO_BITMAP*' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy ALLEGRO_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
// [X] Platform: Clipboard support (from Allegro 5.1.12)
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// Issues:
// [ ] Renderer: The renderer is suboptimal as we need to unindex our buffers and convert vertices manually.
// [ ] Platform: Missing gamepad support.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
struct ALLEGRO_DISPLAY;
union ALLEGRO_EVENT;
IMGUI_IMPL_API bool ImGui_ImplAllegro5_Init(ALLEGRO_DISPLAY* display);
IMGUI_IMPL_API void ImGui_ImplAllegro5_Shutdown();
IMGUI_IMPL_API void ImGui_ImplAllegro5_NewFrame();
IMGUI_IMPL_API void ImGui_ImplAllegro5_RenderDrawData(ImDrawData* draw_data);
IMGUI_IMPL_API bool ImGui_ImplAllegro5_ProcessEvent(ALLEGRO_EVENT* event);
// Use if you want to reset your rendering device without losing Dear ImGui state.
IMGUI_IMPL_API bool ImGui_ImplAllegro5_CreateDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplAllegro5_InvalidateDeviceObjects();

View File

@ -1,276 +0,0 @@
// dear imgui: Platform Binding for Android native app
// This needs to be used along with the OpenGL 3 Renderer (imgui_impl_opengl3)
// Implemented features:
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy AKEYCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
// Missing features:
// [ ] Platform: Clipboard support.
// [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [ ] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android.
// Important:
// - Consider using SDL or GLFW backend on Android, which will be more full-featured than this.
// - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446)
// - FIXME: Unicode character inputs needs to be passed by Dear ImGui by the application (see examples/ and issue #3446)
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago)with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion.
// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+).
// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range.
// 2021-03-04: Initial version.
#include "imgui.h"
#include "imgui_impl_android.h"
#include <time.h>
#include <android/native_window.h>
#include <android/input.h>
#include <android/keycodes.h>
#include <android/log.h>
// Android data
static double g_Time = 0.0;
static ANativeWindow* g_Window;
static char g_LogTag[] = "ImGuiExample";
static ImGuiKey ImGui_ImplAndroid_KeyCodeToImGuiKey(int32_t key_code)
{
switch (key_code)
{
case AKEYCODE_TAB: return ImGuiKey_Tab;
case AKEYCODE_DPAD_LEFT: return ImGuiKey_LeftArrow;
case AKEYCODE_DPAD_RIGHT: return ImGuiKey_RightArrow;
case AKEYCODE_DPAD_UP: return ImGuiKey_UpArrow;
case AKEYCODE_DPAD_DOWN: return ImGuiKey_DownArrow;
case AKEYCODE_PAGE_UP: return ImGuiKey_PageUp;
case AKEYCODE_PAGE_DOWN: return ImGuiKey_PageDown;
case AKEYCODE_MOVE_HOME: return ImGuiKey_Home;
case AKEYCODE_MOVE_END: return ImGuiKey_End;
case AKEYCODE_INSERT: return ImGuiKey_Insert;
case AKEYCODE_FORWARD_DEL: return ImGuiKey_Delete;
case AKEYCODE_DEL: return ImGuiKey_Backspace;
case AKEYCODE_SPACE: return ImGuiKey_Space;
case AKEYCODE_ENTER: return ImGuiKey_Enter;
case AKEYCODE_ESCAPE: return ImGuiKey_Escape;
case AKEYCODE_APOSTROPHE: return ImGuiKey_Apostrophe;
case AKEYCODE_COMMA: return ImGuiKey_Comma;
case AKEYCODE_MINUS: return ImGuiKey_Minus;
case AKEYCODE_PERIOD: return ImGuiKey_Period;
case AKEYCODE_SLASH: return ImGuiKey_Slash;
case AKEYCODE_SEMICOLON: return ImGuiKey_Semicolon;
case AKEYCODE_EQUALS: return ImGuiKey_Equal;
case AKEYCODE_LEFT_BRACKET: return ImGuiKey_LeftBracket;
case AKEYCODE_BACKSLASH: return ImGuiKey_Backslash;
case AKEYCODE_RIGHT_BRACKET: return ImGuiKey_RightBracket;
case AKEYCODE_GRAVE: return ImGuiKey_GraveAccent;
case AKEYCODE_CAPS_LOCK: return ImGuiKey_CapsLock;
case AKEYCODE_SCROLL_LOCK: return ImGuiKey_ScrollLock;
case AKEYCODE_NUM_LOCK: return ImGuiKey_NumLock;
case AKEYCODE_SYSRQ: return ImGuiKey_PrintScreen;
case AKEYCODE_BREAK: return ImGuiKey_Pause;
case AKEYCODE_NUMPAD_0: return ImGuiKey_Keypad0;
case AKEYCODE_NUMPAD_1: return ImGuiKey_Keypad1;
case AKEYCODE_NUMPAD_2: return ImGuiKey_Keypad2;
case AKEYCODE_NUMPAD_3: return ImGuiKey_Keypad3;
case AKEYCODE_NUMPAD_4: return ImGuiKey_Keypad4;
case AKEYCODE_NUMPAD_5: return ImGuiKey_Keypad5;
case AKEYCODE_NUMPAD_6: return ImGuiKey_Keypad6;
case AKEYCODE_NUMPAD_7: return ImGuiKey_Keypad7;
case AKEYCODE_NUMPAD_8: return ImGuiKey_Keypad8;
case AKEYCODE_NUMPAD_9: return ImGuiKey_Keypad9;
case AKEYCODE_NUMPAD_DOT: return ImGuiKey_KeypadDecimal;
case AKEYCODE_NUMPAD_DIVIDE: return ImGuiKey_KeypadDivide;
case AKEYCODE_NUMPAD_MULTIPLY: return ImGuiKey_KeypadMultiply;
case AKEYCODE_NUMPAD_SUBTRACT: return ImGuiKey_KeypadSubtract;
case AKEYCODE_NUMPAD_ADD: return ImGuiKey_KeypadAdd;
case AKEYCODE_NUMPAD_ENTER: return ImGuiKey_KeypadEnter;
case AKEYCODE_NUMPAD_EQUALS: return ImGuiKey_KeypadEqual;
case AKEYCODE_CTRL_LEFT: return ImGuiKey_LeftCtrl;
case AKEYCODE_SHIFT_LEFT: return ImGuiKey_LeftShift;
case AKEYCODE_ALT_LEFT: return ImGuiKey_LeftAlt;
case AKEYCODE_META_LEFT: return ImGuiKey_LeftSuper;
case AKEYCODE_CTRL_RIGHT: return ImGuiKey_RightCtrl;
case AKEYCODE_SHIFT_RIGHT: return ImGuiKey_RightShift;
case AKEYCODE_ALT_RIGHT: return ImGuiKey_RightAlt;
case AKEYCODE_META_RIGHT: return ImGuiKey_RightSuper;
case AKEYCODE_MENU: return ImGuiKey_Menu;
case AKEYCODE_0: return ImGuiKey_0;
case AKEYCODE_1: return ImGuiKey_1;
case AKEYCODE_2: return ImGuiKey_2;
case AKEYCODE_3: return ImGuiKey_3;
case AKEYCODE_4: return ImGuiKey_4;
case AKEYCODE_5: return ImGuiKey_5;
case AKEYCODE_6: return ImGuiKey_6;
case AKEYCODE_7: return ImGuiKey_7;
case AKEYCODE_8: return ImGuiKey_8;
case AKEYCODE_9: return ImGuiKey_9;
case AKEYCODE_A: return ImGuiKey_A;
case AKEYCODE_B: return ImGuiKey_B;
case AKEYCODE_C: return ImGuiKey_C;
case AKEYCODE_D: return ImGuiKey_D;
case AKEYCODE_E: return ImGuiKey_E;
case AKEYCODE_F: return ImGuiKey_F;
case AKEYCODE_G: return ImGuiKey_G;
case AKEYCODE_H: return ImGuiKey_H;
case AKEYCODE_I: return ImGuiKey_I;
case AKEYCODE_J: return ImGuiKey_J;
case AKEYCODE_K: return ImGuiKey_K;
case AKEYCODE_L: return ImGuiKey_L;
case AKEYCODE_M: return ImGuiKey_M;
case AKEYCODE_N: return ImGuiKey_N;
case AKEYCODE_O: return ImGuiKey_O;
case AKEYCODE_P: return ImGuiKey_P;
case AKEYCODE_Q: return ImGuiKey_Q;
case AKEYCODE_R: return ImGuiKey_R;
case AKEYCODE_S: return ImGuiKey_S;
case AKEYCODE_T: return ImGuiKey_T;
case AKEYCODE_U: return ImGuiKey_U;
case AKEYCODE_V: return ImGuiKey_V;
case AKEYCODE_W: return ImGuiKey_W;
case AKEYCODE_X: return ImGuiKey_X;
case AKEYCODE_Y: return ImGuiKey_Y;
case AKEYCODE_Z: return ImGuiKey_Z;
case AKEYCODE_F1: return ImGuiKey_F1;
case AKEYCODE_F2: return ImGuiKey_F2;
case AKEYCODE_F3: return ImGuiKey_F3;
case AKEYCODE_F4: return ImGuiKey_F4;
case AKEYCODE_F5: return ImGuiKey_F5;
case AKEYCODE_F6: return ImGuiKey_F6;
case AKEYCODE_F7: return ImGuiKey_F7;
case AKEYCODE_F8: return ImGuiKey_F8;
case AKEYCODE_F9: return ImGuiKey_F9;
case AKEYCODE_F10: return ImGuiKey_F10;
case AKEYCODE_F11: return ImGuiKey_F11;
case AKEYCODE_F12: return ImGuiKey_F12;
default: return ImGuiKey_None;
}
}
int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event)
{
ImGuiIO& io = ImGui::GetIO();
int32_t event_type = AInputEvent_getType(input_event);
switch (event_type)
{
case AINPUT_EVENT_TYPE_KEY:
{
int32_t event_key_code = AKeyEvent_getKeyCode(input_event);
int32_t event_scan_code = AKeyEvent_getScanCode(input_event);
int32_t event_action = AKeyEvent_getAction(input_event);
int32_t event_meta_state = AKeyEvent_getMetaState(input_event);
io.AddKeyEvent(ImGuiKey_ModCtrl, (event_meta_state & AMETA_CTRL_ON) != 0);
io.AddKeyEvent(ImGuiKey_ModShift, (event_meta_state & AMETA_SHIFT_ON) != 0);
io.AddKeyEvent(ImGuiKey_ModAlt, (event_meta_state & AMETA_ALT_ON) != 0);
io.AddKeyEvent(ImGuiKey_ModSuper, (event_meta_state & AMETA_META_ON) != 0);
switch (event_action)
{
// FIXME: AKEY_EVENT_ACTION_DOWN and AKEY_EVENT_ACTION_UP occur at once as soon as a touch pointer
// goes up from a key. We use a simple key event queue/ and process one event per key per frame in
// ImGui_ImplAndroid_NewFrame()...or consider using IO queue, if suitable: https://github.com/ocornut/imgui/issues/2787
case AKEY_EVENT_ACTION_DOWN:
case AKEY_EVENT_ACTION_UP:
{
ImGuiKey key = ImGui_ImplAndroid_KeyCodeToImGuiKey(event_key_code);
if (key != ImGuiKey_None && (event_action == AKEY_EVENT_ACTION_DOWN || event_action == AKEY_EVENT_ACTION_UP))
{
io.AddKeyEvent(key, event_action == AKEY_EVENT_ACTION_DOWN);
io.SetKeyEventNativeData(key, event_key_code, event_scan_code);
}
break;
}
default:
break;
}
break;
}
case AINPUT_EVENT_TYPE_MOTION:
{
int32_t event_action = AMotionEvent_getAction(input_event);
int32_t event_pointer_index = (event_action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
event_action &= AMOTION_EVENT_ACTION_MASK;
switch (event_action)
{
case AMOTION_EVENT_ACTION_DOWN:
case AMOTION_EVENT_ACTION_UP:
// Physical mouse buttons (and probably other physical devices) also invoke the actions AMOTION_EVENT_ACTION_DOWN/_UP,
// but we have to process them separately to identify the actual button pressed. This is done below via
// AMOTION_EVENT_ACTION_BUTTON_PRESS/_RELEASE. Here, we only process "FINGER" input (and "UNKNOWN", as a fallback).
if((AMotionEvent_getToolType(input_event, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_FINGER)
|| (AMotionEvent_getToolType(input_event, event_pointer_index) == AMOTION_EVENT_TOOL_TYPE_UNKNOWN))
{
io.AddMousePosEvent(AMotionEvent_getX(input_event, event_pointer_index), AMotionEvent_getY(input_event, event_pointer_index));
io.AddMouseButtonEvent(0, event_action == AMOTION_EVENT_ACTION_DOWN);
}
break;
case AMOTION_EVENT_ACTION_BUTTON_PRESS:
case AMOTION_EVENT_ACTION_BUTTON_RELEASE:
{
int32_t button_state = AMotionEvent_getButtonState(input_event);
io.AddMouseButtonEvent(0, (button_state & AMOTION_EVENT_BUTTON_PRIMARY) != 0);
io.AddMouseButtonEvent(1, (button_state & AMOTION_EVENT_BUTTON_SECONDARY) != 0);
io.AddMouseButtonEvent(2, (button_state & AMOTION_EVENT_BUTTON_TERTIARY) != 0);
}
break;
case AMOTION_EVENT_ACTION_HOVER_MOVE: // Hovering: Tool moves while NOT pressed (such as a physical mouse)
case AMOTION_EVENT_ACTION_MOVE: // Touch pointer moves while DOWN
io.AddMousePosEvent(AMotionEvent_getX(input_event, event_pointer_index), AMotionEvent_getY(input_event, event_pointer_index));
break;
case AMOTION_EVENT_ACTION_SCROLL:
io.AddMouseWheelEvent(AMotionEvent_getAxisValue(input_event, AMOTION_EVENT_AXIS_HSCROLL, event_pointer_index), AMotionEvent_getAxisValue(input_event, AMOTION_EVENT_AXIS_VSCROLL, event_pointer_index));
break;
default:
break;
}
}
return 1;
default:
break;
}
return 0;
}
bool ImGui_ImplAndroid_Init(ANativeWindow* window)
{
g_Window = window;
g_Time = 0.0;
// Setup backend capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendPlatformName = "imgui_impl_android";
return true;
}
void ImGui_ImplAndroid_Shutdown()
{
}
void ImGui_ImplAndroid_NewFrame()
{
ImGuiIO& io = ImGui::GetIO();
// Setup display size (every frame to accommodate for window resizing)
int32_t window_width = ANativeWindow_getWidth(g_Window);
int32_t window_height = ANativeWindow_getHeight(g_Window);
int display_width = window_width;
int display_height = window_height;
io.DisplaySize = ImVec2((float)window_width, (float)window_height);
if (window_width > 0 && window_height > 0)
io.DisplayFramebufferScale = ImVec2((float)display_width / window_width, (float)display_height / window_height);
// Setup time step
struct timespec current_timespec;
clock_gettime(CLOCK_MONOTONIC, &current_timespec);
double current_time = (double)(current_timespec.tv_sec) + (current_timespec.tv_nsec / 1000000000.0);
io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f);
g_Time = current_time;
}

View File

@ -1,28 +0,0 @@
// dear imgui: Platform Binding for Android native app
// This needs to be used along with the OpenGL 3 Renderer (imgui_impl_opengl3)
// Implemented features:
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy AKEYCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
// Missing features:
// [ ] Platform: Clipboard support.
// [ ] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [ ] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: Check if this is even possible with Android.
// Important:
// - Consider using SDL or GLFW backend on Android, which will be more full-featured than this.
// - FIXME: On-screen keyboard currently needs to be enabled by the application (see examples/ and issue #3446)
// - FIXME: Unicode character inputs needs to be passed by Dear ImGui by the application (see examples/ and issue #3446)
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
#pragma once
struct ANativeWindow;
struct AInputEvent;
IMGUI_IMPL_API bool ImGui_ImplAndroid_Init(ANativeWindow* window);
IMGUI_IMPL_API int32_t ImGui_ImplAndroid_HandleInputEvent(AInputEvent* input_event);
IMGUI_IMPL_API void ImGui_ImplAndroid_Shutdown();
IMGUI_IMPL_API void ImGui_ImplAndroid_NewFrame();

View File

@ -1,713 +0,0 @@
// dear imgui: Renderer Backend for DirectX10
// This needs to be used along with a Platform Backend (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2022-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
// 2021-05-19: DirectX10: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
// 2021-02-18: DirectX10: Change blending equation to preserve alpha in output buffer.
// 2019-07-21: DirectX10: Backup, clear and restore Geometry Shader is any is bound when calling ImGui_ImplDX10_RenderDrawData().
// 2019-05-29: DirectX10: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: DirectX10: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-07-13: DirectX10: Fixed unreleased resources in Init and Shutdown functions.
// 2018-06-08: Misc: Extracted imgui_impl_dx10.cpp/.h away from the old combined DX10+Win32 example.
// 2018-06-08: DirectX10: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-04-09: Misc: Fixed erroneous call to io.Fonts->ClearInputData() + ClearTexData() that was left in DX10 example but removed in 1.47 (Nov 2015) on other backends.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX10_RenderDrawData() in the .h file so you can call it yourself.
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
// 2016-05-07: DirectX10: Disabling depth-write.
#include "imgui.h"
#include "imgui_impl_dx10.h"
// DirectX
#include <stdio.h>
#include <d3d10_1.h>
#include <d3d10.h>
#include <d3dcompiler.h>
#ifdef _MSC_VER
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
#endif
// DirectX data
struct ImGui_ImplDX10_Data
{
ID3D10Device* pd3dDevice;
IDXGIFactory* pFactory;
ID3D10Buffer* pVB;
ID3D10Buffer* pIB;
ID3D10VertexShader* pVertexShader;
ID3D10InputLayout* pInputLayout;
ID3D10Buffer* pVertexConstantBuffer;
ID3D10PixelShader* pPixelShader;
ID3D10SamplerState* pFontSampler;
ID3D10ShaderResourceView* pFontTextureView;
ID3D10RasterizerState* pRasterizerState;
ID3D10BlendState* pBlendState;
ID3D10DepthStencilState* pDepthStencilState;
int VertexBufferSize;
int IndexBufferSize;
ImGui_ImplDX10_Data() { memset((void*)this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; }
};
struct VERTEX_CONSTANT_BUFFER_DX10
{
float mvp[4][4];
};
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
static ImGui_ImplDX10_Data* ImGui_ImplDX10_GetBackendData()
{
return ImGui::GetCurrentContext() ? (ImGui_ImplDX10_Data*)ImGui::GetIO().BackendRendererUserData : NULL;
}
// Forward Declarations
static void ImGui_ImplDX10_InitPlatformInterface();
static void ImGui_ImplDX10_ShutdownPlatformInterface();
// Functions
static void ImGui_ImplDX10_SetupRenderState(ImDrawData* draw_data, ID3D10Device* ctx)
{
ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
// Setup viewport
D3D10_VIEWPORT vp;
memset(&vp, 0, sizeof(D3D10_VIEWPORT));
vp.Width = (UINT)draw_data->DisplaySize.x;
vp.Height = (UINT)draw_data->DisplaySize.y;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = vp.TopLeftY = 0;
ctx->RSSetViewports(1, &vp);
// Bind shader and vertex buffers
unsigned int stride = sizeof(ImDrawVert);
unsigned int offset = 0;
ctx->IASetInputLayout(bd->pInputLayout);
ctx->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset);
ctx->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
ctx->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx->VSSetShader(bd->pVertexShader);
ctx->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer);
ctx->PSSetShader(bd->pPixelShader);
ctx->PSSetSamplers(0, 1, &bd->pFontSampler);
ctx->GSSetShader(NULL);
// Setup render state
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
ctx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff);
ctx->OMSetDepthStencilState(bd->pDepthStencilState, 0);
ctx->RSSetState(bd->pRasterizerState);
}
// Render function
void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
return;
ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
ID3D10Device* ctx = bd->pd3dDevice;
// Create and grow vertex/index buffers if needed
if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount)
{
if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; }
bd->VertexBufferSize = draw_data->TotalVtxCount + 5000;
D3D10_BUFFER_DESC desc;
memset(&desc, 0, sizeof(D3D10_BUFFER_DESC));
desc.Usage = D3D10_USAGE_DYNAMIC;
desc.ByteWidth = bd->VertexBufferSize * sizeof(ImDrawVert);
desc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
if (ctx->CreateBuffer(&desc, NULL, &bd->pVB) < 0)
return;
}
if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount)
{
if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; }
bd->IndexBufferSize = draw_data->TotalIdxCount + 10000;
D3D10_BUFFER_DESC desc;
memset(&desc, 0, sizeof(D3D10_BUFFER_DESC));
desc.Usage = D3D10_USAGE_DYNAMIC;
desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx);
desc.BindFlags = D3D10_BIND_INDEX_BUFFER;
desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
if (ctx->CreateBuffer(&desc, NULL, &bd->pIB) < 0)
return;
}
// Copy and convert all vertices into a single contiguous buffer
ImDrawVert* vtx_dst = NULL;
ImDrawIdx* idx_dst = NULL;
bd->pVB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&vtx_dst);
bd->pIB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&idx_dst);
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
vtx_dst += cmd_list->VtxBuffer.Size;
idx_dst += cmd_list->IdxBuffer.Size;
}
bd->pVB->Unmap();
bd->pIB->Unmap();
// Setup orthographic projection matrix into our constant buffer
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
{
void* mapped_resource;
if (bd->pVertexConstantBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK)
return;
VERTEX_CONSTANT_BUFFER_DX10* constant_buffer = (VERTEX_CONSTANT_BUFFER_DX10*)mapped_resource;
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
float mvp[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, 0.5f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
};
memcpy(&constant_buffer->mvp, mvp, sizeof(mvp));
bd->pVertexConstantBuffer->Unmap();
}
// Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!)
struct BACKUP_DX10_STATE
{
UINT ScissorRectsCount, ViewportsCount;
D3D10_RECT ScissorRects[D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
D3D10_VIEWPORT Viewports[D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
ID3D10RasterizerState* RS;
ID3D10BlendState* BlendState;
FLOAT BlendFactor[4];
UINT SampleMask;
UINT StencilRef;
ID3D10DepthStencilState* DepthStencilState;
ID3D10ShaderResourceView* PSShaderResource;
ID3D10SamplerState* PSSampler;
ID3D10PixelShader* PS;
ID3D10VertexShader* VS;
ID3D10GeometryShader* GS;
D3D10_PRIMITIVE_TOPOLOGY PrimitiveTopology;
ID3D10Buffer* IndexBuffer, *VertexBuffer, *VSConstantBuffer;
UINT IndexBufferOffset, VertexBufferStride, VertexBufferOffset;
DXGI_FORMAT IndexBufferFormat;
ID3D10InputLayout* InputLayout;
};
BACKUP_DX10_STATE old = {};
old.ScissorRectsCount = old.ViewportsCount = D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects);
ctx->RSGetViewports(&old.ViewportsCount, old.Viewports);
ctx->RSGetState(&old.RS);
ctx->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask);
ctx->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef);
ctx->PSGetShaderResources(0, 1, &old.PSShaderResource);
ctx->PSGetSamplers(0, 1, &old.PSSampler);
ctx->PSGetShader(&old.PS);
ctx->VSGetShader(&old.VS);
ctx->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer);
ctx->GSGetShader(&old.GS);
ctx->IAGetPrimitiveTopology(&old.PrimitiveTopology);
ctx->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset);
ctx->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset);
ctx->IAGetInputLayout(&old.InputLayout);
// Setup desired DX state
ImGui_ImplDX10_SetupRenderState(draw_data, ctx);
// Render command lists
// (Because we merged all buffers into a single one, we maintain our own offset into them)
int global_vtx_offset = 0;
int global_idx_offset = 0;
ImVec2 clip_off = draw_data->DisplayPos;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplDX10_SetupRenderState(draw_data, ctx);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);
ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
continue;
// Apply scissor/clipping rectangle
const D3D10_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y };
ctx->RSSetScissorRects(1, &r);
// Bind texture, Draw
ID3D10ShaderResourceView* texture_srv = (ID3D10ShaderResourceView*)pcmd->GetTexID();
ctx->PSSetShaderResources(0, 1, &texture_srv);
ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
}
}
global_idx_offset += cmd_list->IdxBuffer.Size;
global_vtx_offset += cmd_list->VtxBuffer.Size;
}
// Restore modified DX state
ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
ctx->RSSetViewports(old.ViewportsCount, old.Viewports);
ctx->RSSetState(old.RS); if (old.RS) old.RS->Release();
ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release();
ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release();
ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release();
ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release();
ctx->PSSetShader(old.PS); if (old.PS) old.PS->Release();
ctx->VSSetShader(old.VS); if (old.VS) old.VS->Release();
ctx->GSSetShader(old.GS); if (old.GS) old.GS->Release();
ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release();
ctx->IASetPrimitiveTopology(old.PrimitiveTopology);
ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release();
ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release();
ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release();
}
static void ImGui_ImplDX10_CreateFontsTexture()
{
// Build texture atlas
ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
// Upload texture to graphics system
{
D3D10_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D10_USAGE_DEFAULT;
desc.BindFlags = D3D10_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = 0;
ID3D10Texture2D* pTexture = NULL;
D3D10_SUBRESOURCE_DATA subResource;
subResource.pSysMem = pixels;
subResource.SysMemPitch = desc.Width * 4;
subResource.SysMemSlicePitch = 0;
bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture);
IM_ASSERT(pTexture != NULL);
// Create texture view
D3D10_SHADER_RESOURCE_VIEW_DESC srv_desc;
ZeroMemory(&srv_desc, sizeof(srv_desc));
srv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
srv_desc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D;
srv_desc.Texture2D.MipLevels = desc.MipLevels;
srv_desc.Texture2D.MostDetailedMip = 0;
bd->pd3dDevice->CreateShaderResourceView(pTexture, &srv_desc, &bd->pFontTextureView);
pTexture->Release();
}
// Store our identifier
io.Fonts->SetTexID((ImTextureID)bd->pFontTextureView);
// Create texture sampler
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
{
D3D10_SAMPLER_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Filter = D3D10_FILTER_MIN_MAG_MIP_LINEAR;
desc.AddressU = D3D10_TEXTURE_ADDRESS_WRAP;
desc.AddressV = D3D10_TEXTURE_ADDRESS_WRAP;
desc.AddressW = D3D10_TEXTURE_ADDRESS_WRAP;
desc.MipLODBias = 0.f;
desc.ComparisonFunc = D3D10_COMPARISON_ALWAYS;
desc.MinLOD = 0.f;
desc.MaxLOD = 0.f;
bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler);
}
}
bool ImGui_ImplDX10_CreateDeviceObjects()
{
ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
if (!bd->pd3dDevice)
return false;
if (bd->pFontSampler)
ImGui_ImplDX10_InvalidateDeviceObjects();
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
// If you would like to use this DX10 sample code but remove this dependency you can:
// 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution]
// 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
// See https://github.com/ocornut/imgui/pull/638 for sources and details.
// Create the vertex shader
{
static const char* vertexShader =
"cbuffer vertexBuffer : register(b0) \
{\
float4x4 ProjectionMatrix; \
};\
struct VS_INPUT\
{\
float2 pos : POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
\
struct PS_INPUT\
{\
float4 pos : SV_POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
\
PS_INPUT main(VS_INPUT input)\
{\
PS_INPUT output;\
output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\
output.col = input.col;\
output.uv = input.uv;\
return output;\
}";
ID3DBlob* vertexShaderBlob;
if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &vertexShaderBlob, NULL)))
return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
if (bd->pd3dDevice->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &bd->pVertexShader) != S_OK)
{
vertexShaderBlob->Release();
return false;
}
// Create the input layout
D3D10_INPUT_ELEMENT_DESC local_layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, pos), D3D10_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D10_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D10_INPUT_PER_VERTEX_DATA, 0 },
};
if (bd->pd3dDevice->CreateInputLayout(local_layout, 3, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &bd->pInputLayout) != S_OK)
{
vertexShaderBlob->Release();
return false;
}
vertexShaderBlob->Release();
// Create the constant buffer
{
D3D10_BUFFER_DESC desc;
desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER_DX10);
desc.Usage = D3D10_USAGE_DYNAMIC;
desc.BindFlags = D3D10_BIND_CONSTANT_BUFFER;
desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pVertexConstantBuffer);
}
}
// Create the pixel shader
{
static const char* pixelShader =
"struct PS_INPUT\
{\
float4 pos : SV_POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
sampler sampler0;\
Texture2D texture0;\
\
float4 main(PS_INPUT input) : SV_Target\
{\
float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \
return out_col; \
}";
ID3DBlob* pixelShaderBlob;
if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &pixelShaderBlob, NULL)))
return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
if (bd->pd3dDevice->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), &bd->pPixelShader) != S_OK)
{
pixelShaderBlob->Release();
return false;
}
pixelShaderBlob->Release();
}
// Create the blending setup
{
D3D10_BLEND_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.AlphaToCoverageEnable = false;
desc.BlendEnable[0] = true;
desc.SrcBlend = D3D10_BLEND_SRC_ALPHA;
desc.DestBlend = D3D10_BLEND_INV_SRC_ALPHA;
desc.BlendOp = D3D10_BLEND_OP_ADD;
desc.SrcBlendAlpha = D3D10_BLEND_ONE;
desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA;
desc.BlendOpAlpha = D3D10_BLEND_OP_ADD;
desc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL;
bd->pd3dDevice->CreateBlendState(&desc, &bd->pBlendState);
}
// Create the rasterizer state
{
D3D10_RASTERIZER_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.FillMode = D3D10_FILL_SOLID;
desc.CullMode = D3D10_CULL_NONE;
desc.ScissorEnable = true;
desc.DepthClipEnable = true;
bd->pd3dDevice->CreateRasterizerState(&desc, &bd->pRasterizerState);
}
// Create depth-stencil State
{
D3D10_DEPTH_STENCIL_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.DepthEnable = false;
desc.DepthWriteMask = D3D10_DEPTH_WRITE_MASK_ALL;
desc.DepthFunc = D3D10_COMPARISON_ALWAYS;
desc.StencilEnable = false;
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D10_STENCIL_OP_KEEP;
desc.FrontFace.StencilFunc = D3D10_COMPARISON_ALWAYS;
desc.BackFace = desc.FrontFace;
bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState);
}
ImGui_ImplDX10_CreateFontsTexture();
return true;
}
void ImGui_ImplDX10_InvalidateDeviceObjects()
{
ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
if (!bd->pd3dDevice)
return;
if (bd->pFontSampler) { bd->pFontSampler->Release(); bd->pFontSampler = NULL; }
if (bd->pFontTextureView) { bd->pFontTextureView->Release(); bd->pFontTextureView = NULL; ImGui::GetIO().Fonts->SetTexID(NULL); } // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well.
if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; }
if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; }
if (bd->pBlendState) { bd->pBlendState->Release(); bd->pBlendState = NULL; }
if (bd->pDepthStencilState) { bd->pDepthStencilState->Release(); bd->pDepthStencilState = NULL; }
if (bd->pRasterizerState) { bd->pRasterizerState->Release(); bd->pRasterizerState = NULL; }
if (bd->pPixelShader) { bd->pPixelShader->Release(); bd->pPixelShader = NULL; }
if (bd->pVertexConstantBuffer) { bd->pVertexConstantBuffer->Release(); bd->pVertexConstantBuffer = NULL; }
if (bd->pInputLayout) { bd->pInputLayout->Release(); bd->pInputLayout = NULL; }
if (bd->pVertexShader) { bd->pVertexShader->Release(); bd->pVertexShader = NULL; }
}
bool ImGui_ImplDX10_Init(ID3D10Device* device)
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!");
// Setup backend capabilities flags
ImGui_ImplDX10_Data* bd = IM_NEW(ImGui_ImplDX10_Data)();
io.BackendRendererUserData = (void*)bd;
io.BackendRendererName = "imgui_impl_dx10";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional)
// Get factory from device
IDXGIDevice* pDXGIDevice = NULL;
IDXGIAdapter* pDXGIAdapter = NULL;
IDXGIFactory* pFactory = NULL;
if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK)
if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK)
if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK)
{
bd->pd3dDevice = device;
bd->pFactory = pFactory;
}
if (pDXGIDevice) pDXGIDevice->Release();
if (pDXGIAdapter) pDXGIAdapter->Release();
bd->pd3dDevice->AddRef();
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
ImGui_ImplDX10_InitPlatformInterface();
return true;
}
void ImGui_ImplDX10_Shutdown()
{
ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplDX10_ShutdownPlatformInterface();
ImGui_ImplDX10_InvalidateDeviceObjects();
if (bd->pFactory) { bd->pFactory->Release(); }
if (bd->pd3dDevice) { bd->pd3dDevice->Release(); }
io.BackendRendererName = NULL;
io.BackendRendererUserData = NULL;
IM_DELETE(bd);
}
void ImGui_ImplDX10_NewFrame()
{
ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
IM_ASSERT(bd != NULL && "Did you call ImGui_ImplDX10_Init()?");
if (!bd->pFontSampler)
ImGui_ImplDX10_CreateDeviceObjects();
}
//--------------------------------------------------------------------------------------------------------
// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
//--------------------------------------------------------------------------------------------------------
// Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data.
struct ImGui_ImplDX10_ViewportData
{
IDXGISwapChain* SwapChain;
ID3D10RenderTargetView* RTView;
ImGui_ImplDX10_ViewportData() { SwapChain = NULL; RTView = NULL; }
~ImGui_ImplDX10_ViewportData() { IM_ASSERT(SwapChain == NULL && RTView == NULL); }
};
static void ImGui_ImplDX10_CreateWindow(ImGuiViewport* viewport)
{
ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
ImGui_ImplDX10_ViewportData* vd = IM_NEW(ImGui_ImplDX10_ViewportData)();
viewport->RendererUserData = vd;
// PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*).
// Some backends will leave PlatformHandleRaw NULL, in which case we assume PlatformHandle will contain the HWND.
HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle;
IM_ASSERT(hwnd != 0);
// Create swap chain
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof(sd));
sd.BufferDesc.Width = (UINT)viewport->Size.x;
sd.BufferDesc.Height = (UINT)viewport->Size.y;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.BufferCount = 1;
sd.OutputWindow = hwnd;
sd.Windowed = TRUE;
sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
sd.Flags = 0;
IM_ASSERT(vd->SwapChain == NULL && vd->RTView == NULL);
bd->pFactory->CreateSwapChain(bd->pd3dDevice, &sd, &vd->SwapChain);
// Create the render target
if (vd->SwapChain)
{
ID3D10Texture2D* pBackBuffer;
vd->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
bd->pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &vd->RTView);
pBackBuffer->Release();
}
}
static void ImGui_ImplDX10_DestroyWindow(ImGuiViewport* viewport)
{
// The main viewport (owned by the application) will always have RendererUserData == NULL here since we didn't create the data for it.
if (ImGui_ImplDX10_ViewportData* vd = (ImGui_ImplDX10_ViewportData*)viewport->RendererUserData)
{
if (vd->SwapChain)
vd->SwapChain->Release();
vd->SwapChain = NULL;
if (vd->RTView)
vd->RTView->Release();
vd->RTView = NULL;
IM_DELETE(vd);
}
viewport->RendererUserData = NULL;
}
static void ImGui_ImplDX10_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
{
ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
ImGui_ImplDX10_ViewportData* vd = (ImGui_ImplDX10_ViewportData*)viewport->RendererUserData;
if (vd->RTView)
{
vd->RTView->Release();
vd->RTView = NULL;
}
if (vd->SwapChain)
{
ID3D10Texture2D* pBackBuffer = NULL;
vd->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0);
vd->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
if (pBackBuffer == NULL) { fprintf(stderr, "ImGui_ImplDX10_SetWindowSize() failed creating buffers.\n"); return; }
bd->pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &vd->RTView);
pBackBuffer->Release();
}
}
static void ImGui_ImplDX10_RenderViewport(ImGuiViewport* viewport, void*)
{
ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
ImGui_ImplDX10_ViewportData* vd = (ImGui_ImplDX10_ViewportData*)viewport->RendererUserData;
ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
bd->pd3dDevice->OMSetRenderTargets(1, &vd->RTView, NULL);
if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear))
bd->pd3dDevice->ClearRenderTargetView(vd->RTView, (float*)&clear_color);
ImGui_ImplDX10_RenderDrawData(viewport->DrawData);
}
static void ImGui_ImplDX10_SwapBuffers(ImGuiViewport* viewport, void*)
{
ImGui_ImplDX10_ViewportData* vd = (ImGui_ImplDX10_ViewportData*)viewport->RendererUserData;
vd->SwapChain->Present(0, 0); // Present without vsync
}
void ImGui_ImplDX10_InitPlatformInterface()
{
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
platform_io.Renderer_CreateWindow = ImGui_ImplDX10_CreateWindow;
platform_io.Renderer_DestroyWindow = ImGui_ImplDX10_DestroyWindow;
platform_io.Renderer_SetWindowSize = ImGui_ImplDX10_SetWindowSize;
platform_io.Renderer_RenderWindow = ImGui_ImplDX10_RenderViewport;
platform_io.Renderer_SwapBuffers = ImGui_ImplDX10_SwapBuffers;
}
void ImGui_ImplDX10_ShutdownPlatformInterface()
{
ImGui::DestroyPlatformWindows();
}

View File

@ -1,26 +0,0 @@
// dear imgui: Renderer Backend for DirectX10
// This needs to be used along with a Platform Backend (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
struct ID3D10Device;
IMGUI_IMPL_API bool ImGui_ImplDX10_Init(ID3D10Device* device);
IMGUI_IMPL_API void ImGui_ImplDX10_Shutdown();
IMGUI_IMPL_API void ImGui_ImplDX10_NewFrame();
IMGUI_IMPL_API void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data);
// Use if you want to reset your rendering device without losing Dear ImGui state.
IMGUI_IMPL_API void ImGui_ImplDX10_InvalidateDeviceObjects();
IMGUI_IMPL_API bool ImGui_ImplDX10_CreateDeviceObjects();

View File

@ -1,729 +0,0 @@
// dear imgui: Renderer Backend for DirectX11
// This needs to be used along with a Platform Backend (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2022-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
// 2021-05-19: DirectX11: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
// 2021-02-18: DirectX11: Change blending equation to preserve alpha in output buffer.
// 2019-08-01: DirectX11: Fixed code querying the Geometry Shader state (would generally error with Debug layer enabled).
// 2019-07-21: DirectX11: Backup, clear and restore Geometry Shader is any is bound when calling ImGui_ImplDX10_RenderDrawData. Clearing Hull/Domain/Compute shaders without backup/restore.
// 2019-05-29: DirectX11: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: DirectX11: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-08-01: DirectX11: Querying for IDXGIFactory instead of IDXGIFactory1 to increase compatibility.
// 2018-07-13: DirectX11: Fixed unreleased resources in Init and Shutdown functions.
// 2018-06-08: Misc: Extracted imgui_impl_dx11.cpp/.h away from the old combined DX11+Win32 example.
// 2018-06-08: DirectX11: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX11_RenderDrawData() in the .h file so you can call it yourself.
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
// 2016-05-07: DirectX11: Disabling depth-write.
#include "imgui.h"
#include "imgui_impl_dx11.h"
// DirectX
#include <stdio.h>
#include <d3d11.h>
#include <d3dcompiler.h>
#ifdef _MSC_VER
#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
#endif
// DirectX11 data
struct ImGui_ImplDX11_Data
{
ID3D11Device* pd3dDevice;
ID3D11DeviceContext* pd3dDeviceContext;
IDXGIFactory* pFactory;
ID3D11Buffer* pVB;
ID3D11Buffer* pIB;
ID3D11VertexShader* pVertexShader;
ID3D11InputLayout* pInputLayout;
ID3D11Buffer* pVertexConstantBuffer;
ID3D11PixelShader* pPixelShader;
ID3D11SamplerState* pFontSampler;
ID3D11ShaderResourceView* pFontTextureView;
ID3D11RasterizerState* pRasterizerState;
ID3D11BlendState* pBlendState;
ID3D11DepthStencilState* pDepthStencilState;
int VertexBufferSize;
int IndexBufferSize;
ImGui_ImplDX11_Data() { memset((void*)this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; }
};
struct VERTEX_CONSTANT_BUFFER_DX11
{
float mvp[4][4];
};
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
static ImGui_ImplDX11_Data* ImGui_ImplDX11_GetBackendData()
{
return ImGui::GetCurrentContext() ? (ImGui_ImplDX11_Data*)ImGui::GetIO().BackendRendererUserData : NULL;
}
// Forward Declarations
static void ImGui_ImplDX11_InitPlatformInterface();
static void ImGui_ImplDX11_ShutdownPlatformInterface();
// Functions
static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* ctx)
{
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
// Setup viewport
D3D11_VIEWPORT vp;
memset(&vp, 0, sizeof(D3D11_VIEWPORT));
vp.Width = draw_data->DisplaySize.x;
vp.Height = draw_data->DisplaySize.y;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = vp.TopLeftY = 0;
ctx->RSSetViewports(1, &vp);
// Setup shader and vertex buffers
unsigned int stride = sizeof(ImDrawVert);
unsigned int offset = 0;
ctx->IASetInputLayout(bd->pInputLayout);
ctx->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset);
ctx->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx->VSSetShader(bd->pVertexShader, NULL, 0);
ctx->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer);
ctx->PSSetShader(bd->pPixelShader, NULL, 0);
ctx->PSSetSamplers(0, 1, &bd->pFontSampler);
ctx->GSSetShader(NULL, NULL, 0);
ctx->HSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used..
ctx->DSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used..
ctx->CSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used..
// Setup blend state
const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
ctx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff);
ctx->OMSetDepthStencilState(bd->pDepthStencilState, 0);
ctx->RSSetState(bd->pRasterizerState);
}
// Render function
void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
return;
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
ID3D11DeviceContext* ctx = bd->pd3dDeviceContext;
// Create and grow vertex/index buffers if needed
if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount)
{
if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; }
bd->VertexBufferSize = draw_data->TotalVtxCount + 5000;
D3D11_BUFFER_DESC desc;
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.ByteWidth = bd->VertexBufferSize * sizeof(ImDrawVert);
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
if (bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pVB) < 0)
return;
}
if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount)
{
if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; }
bd->IndexBufferSize = draw_data->TotalIdxCount + 10000;
D3D11_BUFFER_DESC desc;
memset(&desc, 0, sizeof(D3D11_BUFFER_DESC));
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx);
desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
if (bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pIB) < 0)
return;
}
// Upload vertex/index data into a single contiguous GPU buffer
D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource;
if (ctx->Map(bd->pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK)
return;
if (ctx->Map(bd->pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK)
return;
ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData;
ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
vtx_dst += cmd_list->VtxBuffer.Size;
idx_dst += cmd_list->IdxBuffer.Size;
}
ctx->Unmap(bd->pVB, 0);
ctx->Unmap(bd->pIB, 0);
// Setup orthographic projection matrix into our constant buffer
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
{
D3D11_MAPPED_SUBRESOURCE mapped_resource;
if (ctx->Map(bd->pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK)
return;
VERTEX_CONSTANT_BUFFER_DX11* constant_buffer = (VERTEX_CONSTANT_BUFFER_DX11*)mapped_resource.pData;
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
float mvp[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, 0.5f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
};
memcpy(&constant_buffer->mvp, mvp, sizeof(mvp));
ctx->Unmap(bd->pVertexConstantBuffer, 0);
}
// Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!)
struct BACKUP_DX11_STATE
{
UINT ScissorRectsCount, ViewportsCount;
D3D11_RECT ScissorRects[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
D3D11_VIEWPORT Viewports[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
ID3D11RasterizerState* RS;
ID3D11BlendState* BlendState;
FLOAT BlendFactor[4];
UINT SampleMask;
UINT StencilRef;
ID3D11DepthStencilState* DepthStencilState;
ID3D11ShaderResourceView* PSShaderResource;
ID3D11SamplerState* PSSampler;
ID3D11PixelShader* PS;
ID3D11VertexShader* VS;
ID3D11GeometryShader* GS;
UINT PSInstancesCount, VSInstancesCount, GSInstancesCount;
ID3D11ClassInstance *PSInstances[256], *VSInstances[256], *GSInstances[256]; // 256 is max according to PSSetShader documentation
D3D11_PRIMITIVE_TOPOLOGY PrimitiveTopology;
ID3D11Buffer* IndexBuffer, *VertexBuffer, *VSConstantBuffer;
UINT IndexBufferOffset, VertexBufferStride, VertexBufferOffset;
DXGI_FORMAT IndexBufferFormat;
ID3D11InputLayout* InputLayout;
};
BACKUP_DX11_STATE old = {};
old.ScissorRectsCount = old.ViewportsCount = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects);
ctx->RSGetViewports(&old.ViewportsCount, old.Viewports);
ctx->RSGetState(&old.RS);
ctx->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask);
ctx->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef);
ctx->PSGetShaderResources(0, 1, &old.PSShaderResource);
ctx->PSGetSamplers(0, 1, &old.PSSampler);
old.PSInstancesCount = old.VSInstancesCount = old.GSInstancesCount = 256;
ctx->PSGetShader(&old.PS, old.PSInstances, &old.PSInstancesCount);
ctx->VSGetShader(&old.VS, old.VSInstances, &old.VSInstancesCount);
ctx->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer);
ctx->GSGetShader(&old.GS, old.GSInstances, &old.GSInstancesCount);
ctx->IAGetPrimitiveTopology(&old.PrimitiveTopology);
ctx->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset);
ctx->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset);
ctx->IAGetInputLayout(&old.InputLayout);
// Setup desired DX state
ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
// Render command lists
// (Because we merged all buffers into a single one, we maintain our own offset into them)
int global_idx_offset = 0;
int global_vtx_offset = 0;
ImVec2 clip_off = draw_data->DisplayPos;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplDX11_SetupRenderState(draw_data, ctx);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);
ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
continue;
// Apply scissor/clipping rectangle
const D3D11_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y };
ctx->RSSetScissorRects(1, &r);
// Bind texture, Draw
ID3D11ShaderResourceView* texture_srv = (ID3D11ShaderResourceView*)pcmd->GetTexID();
ctx->PSSetShaderResources(0, 1, &texture_srv);
ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
}
}
global_idx_offset += cmd_list->IdxBuffer.Size;
global_vtx_offset += cmd_list->VtxBuffer.Size;
}
// Restore modified DX state
ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
ctx->RSSetViewports(old.ViewportsCount, old.Viewports);
ctx->RSSetState(old.RS); if (old.RS) old.RS->Release();
ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release();
ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release();
ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release();
ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release();
ctx->PSSetShader(old.PS, old.PSInstances, old.PSInstancesCount); if (old.PS) old.PS->Release();
for (UINT i = 0; i < old.PSInstancesCount; i++) if (old.PSInstances[i]) old.PSInstances[i]->Release();
ctx->VSSetShader(old.VS, old.VSInstances, old.VSInstancesCount); if (old.VS) old.VS->Release();
ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release();
ctx->GSSetShader(old.GS, old.GSInstances, old.GSInstancesCount); if (old.GS) old.GS->Release();
for (UINT i = 0; i < old.VSInstancesCount; i++) if (old.VSInstances[i]) old.VSInstances[i]->Release();
ctx->IASetPrimitiveTopology(old.PrimitiveTopology);
ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release();
ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release();
ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release();
}
static void ImGui_ImplDX11_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
// Upload texture to graphics system
{
D3D11_TEXTURE2D_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = 0;
ID3D11Texture2D* pTexture = NULL;
D3D11_SUBRESOURCE_DATA subResource;
subResource.pSysMem = pixels;
subResource.SysMemPitch = desc.Width * 4;
subResource.SysMemSlicePitch = 0;
bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture);
IM_ASSERT(pTexture != NULL);
// Create texture view
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
ZeroMemory(&srvDesc, sizeof(srvDesc));
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = desc.MipLevels;
srvDesc.Texture2D.MostDetailedMip = 0;
bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &bd->pFontTextureView);
pTexture->Release();
}
// Store our identifier
io.Fonts->SetTexID((ImTextureID)bd->pFontTextureView);
// Create texture sampler
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
{
D3D11_SAMPLER_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
desc.MipLODBias = 0.f;
desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
desc.MinLOD = 0.f;
desc.MaxLOD = 0.f;
bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler);
}
}
bool ImGui_ImplDX11_CreateDeviceObjects()
{
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
if (!bd->pd3dDevice)
return false;
if (bd->pFontSampler)
ImGui_ImplDX11_InvalidateDeviceObjects();
// By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
// If you would like to use this DX11 sample code but remove this dependency you can:
// 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution]
// 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
// See https://github.com/ocornut/imgui/pull/638 for sources and details.
// Create the vertex shader
{
static const char* vertexShader =
"cbuffer vertexBuffer : register(b0) \
{\
float4x4 ProjectionMatrix; \
};\
struct VS_INPUT\
{\
float2 pos : POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
\
struct PS_INPUT\
{\
float4 pos : SV_POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
\
PS_INPUT main(VS_INPUT input)\
{\
PS_INPUT output;\
output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\
output.col = input.col;\
output.uv = input.uv;\
return output;\
}";
ID3DBlob* vertexShaderBlob;
if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &vertexShaderBlob, NULL)))
return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
if (bd->pd3dDevice->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), NULL, &bd->pVertexShader) != S_OK)
{
vertexShaderBlob->Release();
return false;
}
// Create the input layout
D3D11_INPUT_ELEMENT_DESC local_layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, pos), D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
if (bd->pd3dDevice->CreateInputLayout(local_layout, 3, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &bd->pInputLayout) != S_OK)
{
vertexShaderBlob->Release();
return false;
}
vertexShaderBlob->Release();
// Create the constant buffer
{
D3D11_BUFFER_DESC desc;
desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER_DX11);
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pVertexConstantBuffer);
}
}
// Create the pixel shader
{
static const char* pixelShader =
"struct PS_INPUT\
{\
float4 pos : SV_POSITION;\
float4 col : COLOR0;\
float2 uv : TEXCOORD0;\
};\
sampler sampler0;\
Texture2D texture0;\
\
float4 main(PS_INPUT input) : SV_Target\
{\
float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \
return out_col; \
}";
ID3DBlob* pixelShaderBlob;
if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &pixelShaderBlob, NULL)))
return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
if (bd->pd3dDevice->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), NULL, &bd->pPixelShader) != S_OK)
{
pixelShaderBlob->Release();
return false;
}
pixelShaderBlob->Release();
}
// Create the blending setup
{
D3D11_BLEND_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.AlphaToCoverageEnable = false;
desc.RenderTarget[0].BlendEnable = true;
desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
bd->pd3dDevice->CreateBlendState(&desc, &bd->pBlendState);
}
// Create the rasterizer state
{
D3D11_RASTERIZER_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.FillMode = D3D11_FILL_SOLID;
desc.CullMode = D3D11_CULL_NONE;
desc.ScissorEnable = true;
desc.DepthClipEnable = true;
bd->pd3dDevice->CreateRasterizerState(&desc, &bd->pRasterizerState);
}
// Create depth-stencil State
{
D3D11_DEPTH_STENCIL_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.DepthEnable = false;
desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
desc.DepthFunc = D3D11_COMPARISON_ALWAYS;
desc.StencilEnable = false;
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
desc.BackFace = desc.FrontFace;
bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState);
}
ImGui_ImplDX11_CreateFontsTexture();
return true;
}
void ImGui_ImplDX11_InvalidateDeviceObjects()
{
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
if (!bd->pd3dDevice)
return;
if (bd->pFontSampler) { bd->pFontSampler->Release(); bd->pFontSampler = NULL; }
if (bd->pFontTextureView) { bd->pFontTextureView->Release(); bd->pFontTextureView = NULL; ImGui::GetIO().Fonts->SetTexID(NULL); } // We copied data->pFontTextureView to io.Fonts->TexID so let's clear that as well.
if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; }
if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; }
if (bd->pBlendState) { bd->pBlendState->Release(); bd->pBlendState = NULL; }
if (bd->pDepthStencilState) { bd->pDepthStencilState->Release(); bd->pDepthStencilState = NULL; }
if (bd->pRasterizerState) { bd->pRasterizerState->Release(); bd->pRasterizerState = NULL; }
if (bd->pPixelShader) { bd->pPixelShader->Release(); bd->pPixelShader = NULL; }
if (bd->pVertexConstantBuffer) { bd->pVertexConstantBuffer->Release(); bd->pVertexConstantBuffer = NULL; }
if (bd->pInputLayout) { bd->pInputLayout->Release(); bd->pInputLayout = NULL; }
if (bd->pVertexShader) { bd->pVertexShader->Release(); bd->pVertexShader = NULL; }
}
bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context)
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!");
// Setup backend capabilities flags
ImGui_ImplDX11_Data* bd = IM_NEW(ImGui_ImplDX11_Data)();
io.BackendRendererUserData = (void*)bd;
io.BackendRendererName = "imgui_impl_dx11";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional)
// Get factory from device
IDXGIDevice* pDXGIDevice = NULL;
IDXGIAdapter* pDXGIAdapter = NULL;
IDXGIFactory* pFactory = NULL;
if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK)
if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK)
if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK)
{
bd->pd3dDevice = device;
bd->pd3dDeviceContext = device_context;
bd->pFactory = pFactory;
}
if (pDXGIDevice) pDXGIDevice->Release();
if (pDXGIAdapter) pDXGIAdapter->Release();
bd->pd3dDevice->AddRef();
bd->pd3dDeviceContext->AddRef();
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
ImGui_ImplDX11_InitPlatformInterface();
return true;
}
void ImGui_ImplDX11_Shutdown()
{
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplDX11_ShutdownPlatformInterface();
ImGui_ImplDX11_InvalidateDeviceObjects();
if (bd->pFactory) { bd->pFactory->Release(); }
if (bd->pd3dDevice) { bd->pd3dDevice->Release(); }
if (bd->pd3dDeviceContext) { bd->pd3dDeviceContext->Release(); }
io.BackendRendererName = NULL;
io.BackendRendererUserData = NULL;
IM_DELETE(bd);
}
void ImGui_ImplDX11_NewFrame()
{
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
IM_ASSERT(bd != NULL && "Did you call ImGui_ImplDX11_Init()?");
if (!bd->pFontSampler)
ImGui_ImplDX11_CreateDeviceObjects();
}
//--------------------------------------------------------------------------------------------------------
// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
//--------------------------------------------------------------------------------------------------------
// Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data.
struct ImGui_ImplDX11_ViewportData
{
IDXGISwapChain* SwapChain;
ID3D11RenderTargetView* RTView;
ImGui_ImplDX11_ViewportData() { SwapChain = NULL; RTView = NULL; }
~ImGui_ImplDX11_ViewportData() { IM_ASSERT(SwapChain == NULL && RTView == NULL); }
};
static void ImGui_ImplDX11_CreateWindow(ImGuiViewport* viewport)
{
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
ImGui_ImplDX11_ViewportData* vd = IM_NEW(ImGui_ImplDX11_ViewportData)();
viewport->RendererUserData = vd;
// PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*).
// Some backend will leave PlatformHandleRaw NULL, in which case we assume PlatformHandle will contain the HWND.
HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle;
IM_ASSERT(hwnd != 0);
// Create swap chain
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof(sd));
sd.BufferDesc.Width = (UINT)viewport->Size.x;
sd.BufferDesc.Height = (UINT)viewport->Size.y;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.BufferCount = 1;
sd.OutputWindow = hwnd;
sd.Windowed = TRUE;
sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
sd.Flags = 0;
IM_ASSERT(vd->SwapChain == NULL && vd->RTView == NULL);
bd->pFactory->CreateSwapChain(bd->pd3dDevice, &sd, &vd->SwapChain);
// Create the render target
if (vd->SwapChain)
{
ID3D11Texture2D* pBackBuffer;
vd->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
bd->pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &vd->RTView);
pBackBuffer->Release();
}
}
static void ImGui_ImplDX11_DestroyWindow(ImGuiViewport* viewport)
{
// The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it.
if (ImGui_ImplDX11_ViewportData* vd = (ImGui_ImplDX11_ViewportData*)viewport->RendererUserData)
{
if (vd->SwapChain)
vd->SwapChain->Release();
vd->SwapChain = NULL;
if (vd->RTView)
vd->RTView->Release();
vd->RTView = NULL;
IM_DELETE(vd);
}
viewport->RendererUserData = NULL;
}
static void ImGui_ImplDX11_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
{
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
ImGui_ImplDX11_ViewportData* vd = (ImGui_ImplDX11_ViewportData*)viewport->RendererUserData;
if (vd->RTView)
{
vd->RTView->Release();
vd->RTView = NULL;
}
if (vd->SwapChain)
{
ID3D11Texture2D* pBackBuffer = NULL;
vd->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0);
vd->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
if (pBackBuffer == NULL) { fprintf(stderr, "ImGui_ImplDX11_SetWindowSize() failed creating buffers.\n"); return; }
bd->pd3dDevice->CreateRenderTargetView(pBackBuffer, NULL, &vd->RTView);
pBackBuffer->Release();
}
}
static void ImGui_ImplDX11_RenderWindow(ImGuiViewport* viewport, void*)
{
ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData();
ImGui_ImplDX11_ViewportData* vd = (ImGui_ImplDX11_ViewportData*)viewport->RendererUserData;
ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
bd->pd3dDeviceContext->OMSetRenderTargets(1, &vd->RTView, NULL);
if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear))
bd->pd3dDeviceContext->ClearRenderTargetView(vd->RTView, (float*)&clear_color);
ImGui_ImplDX11_RenderDrawData(viewport->DrawData);
}
static void ImGui_ImplDX11_SwapBuffers(ImGuiViewport* viewport, void*)
{
ImGui_ImplDX11_ViewportData* vd = (ImGui_ImplDX11_ViewportData*)viewport->RendererUserData;
vd->SwapChain->Present(0, 0); // Present without vsync
}
static void ImGui_ImplDX11_InitPlatformInterface()
{
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
platform_io.Renderer_CreateWindow = ImGui_ImplDX11_CreateWindow;
platform_io.Renderer_DestroyWindow = ImGui_ImplDX11_DestroyWindow;
platform_io.Renderer_SetWindowSize = ImGui_ImplDX11_SetWindowSize;
platform_io.Renderer_RenderWindow = ImGui_ImplDX11_RenderWindow;
platform_io.Renderer_SwapBuffers = ImGui_ImplDX11_SwapBuffers;
}
static void ImGui_ImplDX11_ShutdownPlatformInterface()
{
ImGui::DestroyPlatformWindows();
}

View File

@ -1,27 +0,0 @@
// dear imgui: Renderer Backend for DirectX11
// This needs to be used along with a Platform Backend (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
struct ID3D11Device;
struct ID3D11DeviceContext;
IMGUI_IMPL_API bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context);
IMGUI_IMPL_API void ImGui_ImplDX11_Shutdown();
IMGUI_IMPL_API void ImGui_ImplDX11_NewFrame();
IMGUI_IMPL_API void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data);
// Use if you want to reset your rendering device without losing Dear ImGui state.
IMGUI_IMPL_API void ImGui_ImplDX11_InvalidateDeviceObjects();
IMGUI_IMPL_API bool ImGui_ImplDX11_CreateDeviceObjects();

View File

@ -1,39 +0,0 @@
// dear imgui: Renderer Backend for DirectX12
// This needs to be used along with a Platform Backend (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
// Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'.
// See imgui_impl_dx12.cpp file for details.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
#include <dxgiformat.h> // DXGI_FORMAT
struct ID3D12Device;
struct ID3D12DescriptorHeap;
struct ID3D12GraphicsCommandList;
struct D3D12_CPU_DESCRIPTOR_HANDLE;
struct D3D12_GPU_DESCRIPTOR_HANDLE;
// cmd_list is the command list that the implementation will use to render imgui draw lists.
// Before calling the render function, caller must prepare cmd_list by resetting it and setting the appropriate
// render target and descriptor heap that contains font_srv_cpu_desc_handle/font_srv_gpu_desc_handle.
// font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture.
IMGUI_IMPL_API bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* cbv_srv_heap,
D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle);
IMGUI_IMPL_API void ImGui_ImplDX12_Shutdown();
IMGUI_IMPL_API void ImGui_ImplDX12_NewFrame();
IMGUI_IMPL_API void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* graphics_command_list);
// Use if you want to reset your rendering device without losing Dear ImGui state.
IMGUI_IMPL_API void ImGui_ImplDX12_InvalidateDeviceObjects();
IMGUI_IMPL_API bool ImGui_ImplDX12_CreateDeviceObjects();

View File

@ -1,540 +0,0 @@
// dear imgui: Renderer Backend for DirectX9
// This needs to be used along with a Platform Backend (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2022-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
// 2021-06-25: DirectX9: Explicitly disable texture state stages after >= 1.
// 2021-05-19: DirectX9: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
// 2021-04-23: DirectX9: Explicitly setting up more graphics states to increase compatibility with unusual non-default states.
// 2021-03-18: DirectX9: Calling IDirect3DStateBlock9::Capture() after CreateStateBlock() as a workaround for state restoring issues (see #3857).
// 2021-03-03: DirectX9: Added support for IMGUI_USE_BGRA_PACKED_COLOR in user's imconfig file.
// 2021-02-18: DirectX9: Change blending equation to preserve alpha in output buffer.
// 2019-05-29: DirectX9: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: DirectX9: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2019-03-29: Misc: Fixed erroneous assert in ImGui_ImplDX9_InvalidateDeviceObjects().
// 2019-01-16: Misc: Disabled fog before drawing UI's. Fixes issue #2288.
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-06-08: Misc: Extracted imgui_impl_dx9.cpp/.h away from the old combined DX9+Win32 example.
// 2018-06-08: DirectX9: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-05-07: Render: Saving/restoring Transform because they don't seem to be included in the StateBlock. Setting shading mode to Gouraud.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX9_RenderDrawData() in the .h file so you can call it yourself.
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
#include "imgui.h"
#include "imgui_impl_dx9.h"
// DirectX
#include <d3d9.h>
// DirectX data
struct ImGui_ImplDX9_Data
{
LPDIRECT3DDEVICE9 pd3dDevice;
LPDIRECT3DVERTEXBUFFER9 pVB;
LPDIRECT3DINDEXBUFFER9 pIB;
LPDIRECT3DTEXTURE9 FontTexture;
int VertexBufferSize;
int IndexBufferSize;
ImGui_ImplDX9_Data() { memset((void*)this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; }
};
struct CUSTOMVERTEX
{
float pos[3];
D3DCOLOR col;
float uv[2];
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)
#ifdef IMGUI_USE_BGRA_PACKED_COLOR
#define IMGUI_COL_TO_DX9_ARGB(_COL) (_COL)
#else
#define IMGUI_COL_TO_DX9_ARGB(_COL) (((_COL) & 0xFF00FF00) | (((_COL) & 0xFF0000) >> 16) | (((_COL) & 0xFF) << 16))
#endif
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
static ImGui_ImplDX9_Data* ImGui_ImplDX9_GetBackendData()
{
return ImGui::GetCurrentContext() ? (ImGui_ImplDX9_Data*)ImGui::GetIO().BackendRendererUserData : NULL;
}
// Forward Declarations
static void ImGui_ImplDX9_InitPlatformInterface();
static void ImGui_ImplDX9_ShutdownPlatformInterface();
static void ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows();
static void ImGui_ImplDX9_InvalidateDeviceObjectsForPlatformWindows();
// Functions
static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data)
{
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
// Setup viewport
D3DVIEWPORT9 vp;
vp.X = vp.Y = 0;
vp.Width = (DWORD)draw_data->DisplaySize.x;
vp.Height = (DWORD)draw_data->DisplaySize.y;
vp.MinZ = 0.0f;
vp.MaxZ = 1.0f;
bd->pd3dDevice->SetViewport(&vp);
// Setup render state: fixed-pipeline, alpha-blending, no face culling, no depth testing, shade mode (for gradient), bilinear sampling.
bd->pd3dDevice->SetPixelShader(NULL);
bd->pd3dDevice->SetVertexShader(NULL);
bd->pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
bd->pd3dDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
bd->pd3dDevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
bd->pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
bd->pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
bd->pd3dDevice->SetRenderState(D3DRS_ZENABLE, FALSE);
bd->pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
bd->pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
bd->pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
bd->pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
bd->pd3dDevice->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE);
bd->pd3dDevice->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE);
bd->pd3dDevice->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA);
bd->pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE);
bd->pd3dDevice->SetRenderState(D3DRS_FOGENABLE, FALSE);
bd->pd3dDevice->SetRenderState(D3DRS_RANGEFOGENABLE, FALSE);
bd->pd3dDevice->SetRenderState(D3DRS_SPECULARENABLE, FALSE);
bd->pd3dDevice->SetRenderState(D3DRS_STENCILENABLE, FALSE);
bd->pd3dDevice->SetRenderState(D3DRS_CLIPPING, TRUE);
bd->pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
bd->pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
bd->pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
bd->pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
bd->pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
bd->pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
bd->pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
bd->pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
bd->pd3dDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
bd->pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
bd->pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
// Setup orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
// Being agnostic of whether <d3dx9.h> or <DirectXMath.h> can be used, we aren't relying on D3DXMatrixIdentity()/D3DXMatrixOrthoOffCenterLH() or DirectX::XMMatrixIdentity()/DirectX::XMMatrixOrthographicOffCenterLH()
{
float L = draw_data->DisplayPos.x + 0.5f;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x + 0.5f;
float T = draw_data->DisplayPos.y + 0.5f;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y + 0.5f;
D3DMATRIX mat_identity = { { { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f } } };
D3DMATRIX mat_projection =
{ { {
2.0f/(R-L), 0.0f, 0.0f, 0.0f,
0.0f, 2.0f/(T-B), 0.0f, 0.0f,
0.0f, 0.0f, 0.5f, 0.0f,
(L+R)/(L-R), (T+B)/(B-T), 0.5f, 1.0f
} } };
bd->pd3dDevice->SetTransform(D3DTS_WORLD, &mat_identity);
bd->pd3dDevice->SetTransform(D3DTS_VIEW, &mat_identity);
bd->pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_projection);
}
}
// Render function.
void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized
if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
return;
// Create and grow buffers if needed
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount)
{
if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; }
bd->VertexBufferSize = draw_data->TotalVtxCount + 5000;
if (bd->pd3dDevice->CreateVertexBuffer(bd->VertexBufferSize * sizeof(CUSTOMVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &bd->pVB, NULL) < 0)
return;
}
if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount)
{
if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; }
bd->IndexBufferSize = draw_data->TotalIdxCount + 10000;
if (bd->pd3dDevice->CreateIndexBuffer(bd->IndexBufferSize * sizeof(ImDrawIdx), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, sizeof(ImDrawIdx) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32, D3DPOOL_DEFAULT, &bd->pIB, NULL) < 0)
return;
}
// Backup the DX9 state
IDirect3DStateBlock9* d3d9_state_block = NULL;
if (bd->pd3dDevice->CreateStateBlock(D3DSBT_ALL, &d3d9_state_block) < 0)
return;
if (d3d9_state_block->Capture() < 0)
{
d3d9_state_block->Release();
return;
}
// Backup the DX9 transform (DX9 documentation suggests that it is included in the StateBlock but it doesn't appear to)
D3DMATRIX last_world, last_view, last_projection;
bd->pd3dDevice->GetTransform(D3DTS_WORLD, &last_world);
bd->pd3dDevice->GetTransform(D3DTS_VIEW, &last_view);
bd->pd3dDevice->GetTransform(D3DTS_PROJECTION, &last_projection);
// Allocate buffers
CUSTOMVERTEX* vtx_dst;
ImDrawIdx* idx_dst;
if (bd->pVB->Lock(0, (UINT)(draw_data->TotalVtxCount * sizeof(CUSTOMVERTEX)), (void**)&vtx_dst, D3DLOCK_DISCARD) < 0)
{
d3d9_state_block->Release();
return;
}
if (bd->pIB->Lock(0, (UINT)(draw_data->TotalIdxCount * sizeof(ImDrawIdx)), (void**)&idx_dst, D3DLOCK_DISCARD) < 0)
{
bd->pVB->Unlock();
d3d9_state_block->Release();
return;
}
// Copy and convert all vertices into a single contiguous buffer, convert colors to DX9 default format.
// FIXME-OPT: This is a minor waste of resource, the ideal is to use imconfig.h and
// 1) to avoid repacking colors: #define IMGUI_USE_BGRA_PACKED_COLOR
// 2) to avoid repacking vertices: #define IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert { ImVec2 pos; float z; ImU32 col; ImVec2 uv; }
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
const ImDrawVert* vtx_src = cmd_list->VtxBuffer.Data;
for (int i = 0; i < cmd_list->VtxBuffer.Size; i++)
{
vtx_dst->pos[0] = vtx_src->pos.x;
vtx_dst->pos[1] = vtx_src->pos.y;
vtx_dst->pos[2] = 0.0f;
vtx_dst->col = IMGUI_COL_TO_DX9_ARGB(vtx_src->col);
vtx_dst->uv[0] = vtx_src->uv.x;
vtx_dst->uv[1] = vtx_src->uv.y;
vtx_dst++;
vtx_src++;
}
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
idx_dst += cmd_list->IdxBuffer.Size;
}
bd->pVB->Unlock();
bd->pIB->Unlock();
bd->pd3dDevice->SetStreamSource(0, bd->pVB, 0, sizeof(CUSTOMVERTEX));
bd->pd3dDevice->SetIndices(bd->pIB);
bd->pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
// Setup desired DX state
ImGui_ImplDX9_SetupRenderState(draw_data);
// Render command lists
// (Because we merged all buffers into a single one, we maintain our own offset into them)
int global_vtx_offset = 0;
int global_idx_offset = 0;
ImVec2 clip_off = draw_data->DisplayPos;
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplDX9_SetupRenderState(draw_data);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y);
ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y);
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
continue;
// Apply Scissor/clipping rectangle, Bind texture, Draw
const RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y };
const LPDIRECT3DTEXTURE9 texture = (LPDIRECT3DTEXTURE9)pcmd->GetTexID();
bd->pd3dDevice->SetTexture(0, texture);
bd->pd3dDevice->SetScissorRect(&r);
bd->pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, pcmd->VtxOffset + global_vtx_offset, 0, (UINT)cmd_list->VtxBuffer.Size, pcmd->IdxOffset + global_idx_offset, pcmd->ElemCount / 3);
}
}
global_idx_offset += cmd_list->IdxBuffer.Size;
global_vtx_offset += cmd_list->VtxBuffer.Size;
}
// When using multi-viewports, it appears that there's an odd logic in DirectX9 which prevent subsequent windows
// from rendering until the first window submits at least one draw call, even once. That's our workaround. (see #2560)
if (global_vtx_offset == 0)
bd->pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 0, 0, 0);
// Restore the DX9 transform
bd->pd3dDevice->SetTransform(D3DTS_WORLD, &last_world);
bd->pd3dDevice->SetTransform(D3DTS_VIEW, &last_view);
bd->pd3dDevice->SetTransform(D3DTS_PROJECTION, &last_projection);
// Restore the DX9 state
d3d9_state_block->Apply();
d3d9_state_block->Release();
}
bool ImGui_ImplDX9_Init(IDirect3DDevice9* device)
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!");
// Setup backend capabilities flags
ImGui_ImplDX9_Data* bd = IM_NEW(ImGui_ImplDX9_Data)();
io.BackendRendererUserData = (void*)bd;
io.BackendRendererName = "imgui_impl_dx9";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional)
bd->pd3dDevice = device;
bd->pd3dDevice->AddRef();
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
ImGui_ImplDX9_InitPlatformInterface();
return true;
}
void ImGui_ImplDX9_Shutdown()
{
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplDX9_ShutdownPlatformInterface();
ImGui_ImplDX9_InvalidateDeviceObjects();
if (bd->pd3dDevice) { bd->pd3dDevice->Release(); }
io.BackendRendererName = NULL;
io.BackendRendererUserData = NULL;
IM_DELETE(bd);
}
static bool ImGui_ImplDX9_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
unsigned char* pixels;
int width, height, bytes_per_pixel;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &bytes_per_pixel);
// Convert RGBA32 to BGRA32 (because RGBA32 is not well supported by DX9 devices)
#ifndef IMGUI_USE_BGRA_PACKED_COLOR
if (io.Fonts->TexPixelsUseColors)
{
ImU32* dst_start = (ImU32*)ImGui::MemAlloc((size_t)width * height * bytes_per_pixel);
for (ImU32* src = (ImU32*)pixels, *dst = dst_start, *dst_end = dst_start + (size_t)width * height; dst < dst_end; src++, dst++)
*dst = IMGUI_COL_TO_DX9_ARGB(*src);
pixels = (unsigned char*)dst_start;
}
#endif
// Upload texture to graphics system
bd->FontTexture = NULL;
if (bd->pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &bd->FontTexture, NULL) < 0)
return false;
D3DLOCKED_RECT tex_locked_rect;
if (bd->FontTexture->LockRect(0, &tex_locked_rect, NULL, 0) != D3D_OK)
return false;
for (int y = 0; y < height; y++)
memcpy((unsigned char*)tex_locked_rect.pBits + (size_t)tex_locked_rect.Pitch * y, pixels + (size_t)width * bytes_per_pixel * y, (size_t)width * bytes_per_pixel);
bd->FontTexture->UnlockRect(0);
// Store our identifier
io.Fonts->SetTexID((ImTextureID)bd->FontTexture);
#ifndef IMGUI_USE_BGRA_PACKED_COLOR
if (io.Fonts->TexPixelsUseColors)
ImGui::MemFree(pixels);
#endif
return true;
}
bool ImGui_ImplDX9_CreateDeviceObjects()
{
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
if (!bd || !bd->pd3dDevice)
return false;
if (!ImGui_ImplDX9_CreateFontsTexture())
return false;
ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows();
return true;
}
void ImGui_ImplDX9_InvalidateDeviceObjects()
{
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
if (!bd || !bd->pd3dDevice)
return;
if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; }
if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; }
if (bd->FontTexture) { bd->FontTexture->Release(); bd->FontTexture = NULL; ImGui::GetIO().Fonts->SetTexID(NULL); } // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well.
ImGui_ImplDX9_InvalidateDeviceObjectsForPlatformWindows();
}
void ImGui_ImplDX9_NewFrame()
{
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
IM_ASSERT(bd != NULL && "Did you call ImGui_ImplDX9_Init()?");
if (!bd->FontTexture)
ImGui_ImplDX9_CreateDeviceObjects();
}
//--------------------------------------------------------------------------------------------------------
// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
//--------------------------------------------------------------------------------------------------------
// Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data.
struct ImGui_ImplDX9_ViewportData
{
IDirect3DSwapChain9* SwapChain;
D3DPRESENT_PARAMETERS d3dpp;
ImGui_ImplDX9_ViewportData() { SwapChain = NULL; ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS)); }
~ImGui_ImplDX9_ViewportData() { IM_ASSERT(SwapChain == NULL); }
};
static void ImGui_ImplDX9_CreateWindow(ImGuiViewport* viewport)
{
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
ImGui_ImplDX9_ViewportData* vd = IM_NEW(ImGui_ImplDX9_ViewportData)();
viewport->RendererUserData = vd;
// PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*).
// Some backends will leave PlatformHandleRaw NULL, in which case we assume PlatformHandle will contain the HWND.
HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle;
IM_ASSERT(hwnd != 0);
ZeroMemory(&vd->d3dpp, sizeof(D3DPRESENT_PARAMETERS));
vd->d3dpp.Windowed = TRUE;
vd->d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
vd->d3dpp.BackBufferWidth = (UINT)viewport->Size.x;
vd->d3dpp.BackBufferHeight = (UINT)viewport->Size.y;
vd->d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
vd->d3dpp.hDeviceWindow = hwnd;
vd->d3dpp.EnableAutoDepthStencil = FALSE;
vd->d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
vd->d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; // Present without vsync
HRESULT hr = bd->pd3dDevice->CreateAdditionalSwapChain(&vd->d3dpp, &vd->SwapChain); IM_UNUSED(hr);
IM_ASSERT(hr == D3D_OK);
IM_ASSERT(vd->SwapChain != NULL);
}
static void ImGui_ImplDX9_DestroyWindow(ImGuiViewport* viewport)
{
// The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it.
if (ImGui_ImplDX9_ViewportData* vd = (ImGui_ImplDX9_ViewportData*)viewport->RendererUserData)
{
if (vd->SwapChain)
vd->SwapChain->Release();
vd->SwapChain = NULL;
ZeroMemory(&vd->d3dpp, sizeof(D3DPRESENT_PARAMETERS));
IM_DELETE(vd);
}
viewport->RendererUserData = NULL;
}
static void ImGui_ImplDX9_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
{
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
ImGui_ImplDX9_ViewportData* vd = (ImGui_ImplDX9_ViewportData*)viewport->RendererUserData;
if (vd->SwapChain)
{
vd->SwapChain->Release();
vd->SwapChain = NULL;
vd->d3dpp.BackBufferWidth = (UINT)size.x;
vd->d3dpp.BackBufferHeight = (UINT)size.y;
HRESULT hr = bd->pd3dDevice->CreateAdditionalSwapChain(&vd->d3dpp, &vd->SwapChain); IM_UNUSED(hr);
IM_ASSERT(hr == D3D_OK);
}
}
static void ImGui_ImplDX9_RenderWindow(ImGuiViewport* viewport, void*)
{
ImGui_ImplDX9_Data* bd = ImGui_ImplDX9_GetBackendData();
ImGui_ImplDX9_ViewportData* vd = (ImGui_ImplDX9_ViewportData*)viewport->RendererUserData;
ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
LPDIRECT3DSURFACE9 render_target = NULL;
LPDIRECT3DSURFACE9 last_render_target = NULL;
LPDIRECT3DSURFACE9 last_depth_stencil = NULL;
vd->SwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &render_target);
bd->pd3dDevice->GetRenderTarget(0, &last_render_target);
bd->pd3dDevice->GetDepthStencilSurface(&last_depth_stencil);
bd->pd3dDevice->SetRenderTarget(0, render_target);
bd->pd3dDevice->SetDepthStencilSurface(NULL);
if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear))
{
D3DCOLOR clear_col_dx = D3DCOLOR_RGBA((int)(clear_color.x*255.0f), (int)(clear_color.y*255.0f), (int)(clear_color.z*255.0f), (int)(clear_color.w*255.0f));
bd->pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, clear_col_dx, 1.0f, 0);
}
ImGui_ImplDX9_RenderDrawData(viewport->DrawData);
// Restore render target
bd->pd3dDevice->SetRenderTarget(0, last_render_target);
bd->pd3dDevice->SetDepthStencilSurface(last_depth_stencil);
render_target->Release();
last_render_target->Release();
if (last_depth_stencil) last_depth_stencil->Release();
}
static void ImGui_ImplDX9_SwapBuffers(ImGuiViewport* viewport, void*)
{
ImGui_ImplDX9_ViewportData* vd = (ImGui_ImplDX9_ViewportData*)viewport->RendererUserData;
HRESULT hr = vd->SwapChain->Present(NULL, NULL, vd->d3dpp.hDeviceWindow, NULL, 0);
// Let main application handle D3DERR_DEVICELOST by resetting the device.
IM_ASSERT(hr == D3D_OK || hr == D3DERR_DEVICELOST);
}
static void ImGui_ImplDX9_InitPlatformInterface()
{
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
platform_io.Renderer_CreateWindow = ImGui_ImplDX9_CreateWindow;
platform_io.Renderer_DestroyWindow = ImGui_ImplDX9_DestroyWindow;
platform_io.Renderer_SetWindowSize = ImGui_ImplDX9_SetWindowSize;
platform_io.Renderer_RenderWindow = ImGui_ImplDX9_RenderWindow;
platform_io.Renderer_SwapBuffers = ImGui_ImplDX9_SwapBuffers;
}
static void ImGui_ImplDX9_ShutdownPlatformInterface()
{
ImGui::DestroyPlatformWindows();
}
static void ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows()
{
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
for (int i = 1; i < platform_io.Viewports.Size; i++)
if (!platform_io.Viewports[i]->RendererUserData)
ImGui_ImplDX9_CreateWindow(platform_io.Viewports[i]);
}
static void ImGui_ImplDX9_InvalidateDeviceObjectsForPlatformWindows()
{
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
for (int i = 1; i < platform_io.Viewports.Size; i++)
if (platform_io.Viewports[i]->RendererUserData)
ImGui_ImplDX9_DestroyWindow(platform_io.Viewports[i]);
}

View File

@ -1,26 +0,0 @@
// dear imgui: Renderer Backend for DirectX9
// This needs to be used along with a Platform Backend (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
struct IDirect3DDevice9;
IMGUI_IMPL_API bool ImGui_ImplDX9_Init(IDirect3DDevice9* device);
IMGUI_IMPL_API void ImGui_ImplDX9_Shutdown();
IMGUI_IMPL_API void ImGui_ImplDX9_NewFrame();
IMGUI_IMPL_API void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data);
// Use if you want to reset your rendering device without losing Dear ImGui state.
IMGUI_IMPL_API bool ImGui_ImplDX9_CreateDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplDX9_InvalidateDeviceObjects();

View File

@ -1,51 +0,0 @@
// dear imgui: Platform Backend for GLFW
// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan, WebGPU..)
// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.)
// (Requires: GLFW 3.1+. Prefer GLFW 3.3+ for full feature support.)
// Implemented features:
// [X] Platform: Clipboard support.
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
// [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange' (note: the resizing cursors requires GLFW 3.4+).
// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// Issues:
// [ ] Platform: Multi-viewport support: ParentViewportID not honored, and so io.ConfigViewportsNoDefaultParent has no effect (minor).
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// About GLSL version:
// The 'glsl_version' initialization parameter defaults to "#version 150" if NULL.
// Only override if your GL version doesn't handle this GLSL version. Keep NULL if unsure!
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
struct GLFWwindow;
struct GLFWmonitor;
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks);
extern "C" IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks);
IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks);
IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown();
extern "C" IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame();
// GLFW callbacks (installer)
// - When calling Init with 'install_callbacks=true': ImGui_ImplGlfw_InstallCallbacks() is called. GLFW callbacks will be installed for you. They will chain-call user's previously installed callbacks, if any.
// - When calling Init with 'install_callbacks=false': GLFW callbacks won't be installed. You will need to call individual function yourself from your own GLFW callbacks.
IMGUI_IMPL_API void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window);
IMGUI_IMPL_API void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window);
// GLFW callbacks (individual callbacks to call if you didn't install callbacks)
IMGUI_IMPL_API void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused); // Since 1.84
IMGUI_IMPL_API void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered); // Since 1.84
IMGUI_IMPL_API void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y); // Since 1.87
IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods);
IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset);
IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c);
IMGUI_IMPL_API void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor* monitor, int event);

View File

@ -1,297 +0,0 @@
// dear imgui: Platform Backend for GLUT/FreeGLUT
// This needs to be used along with a Renderer (e.g. OpenGL2)
// !!! GLUT/FreeGLUT IS OBSOLETE PREHISTORIC SOFTWARE. Using GLUT is not recommended unless you really miss the 90's. !!!
// !!! If someone or something is teaching you GLUT today, you are being abused. Please show some resistance. !!!
// !!! Nowadays, prefer using GLFW or SDL instead!
// Implemented features:
// [X] Platform: Partial keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLUT values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
// Issues:
// [ ] Platform: GLUT is unable to distinguish e.g. Backspace from CTRL+H or TAB from CTRL+I
// [ ] Platform: Missing mouse cursor shape/visibility support.
// [ ] Platform: Missing clipboard support (not supported by Glut).
// [ ] Platform: Missing gamepad support.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion.
// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+).
// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range.
// 2019-04-03: Misc: Renamed imgui_impl_freeglut.cpp/.h to imgui_impl_glut.cpp/.h.
// 2019-03-25: Misc: Made io.DeltaTime always above zero.
// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
// 2018-03-22: Added GLUT Platform binding.
#include "imgui.h"
#include "imgui_impl_glut.h"
#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/freeglut.h>
#endif
#ifdef _MSC_VER
#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
#endif
static int g_Time = 0; // Current time, in milliseconds
// Glut has 1 function for characters and one for "special keys". We map the characters in the 0..255 range and the keys above.
static ImGuiKey ImGui_ImplGLUT_KeyToImGuiKey(int key)
{
switch (key)
{
case '\t': return ImGuiKey_Tab;
case 256 + GLUT_KEY_LEFT: return ImGuiKey_LeftArrow;
case 256 + GLUT_KEY_RIGHT: return ImGuiKey_RightArrow;
case 256 + GLUT_KEY_UP: return ImGuiKey_UpArrow;
case 256 + GLUT_KEY_DOWN: return ImGuiKey_DownArrow;
case 256 + GLUT_KEY_PAGE_UP: return ImGuiKey_PageUp;
case 256 + GLUT_KEY_PAGE_DOWN: return ImGuiKey_PageDown;
case 256 + GLUT_KEY_HOME: return ImGuiKey_Home;
case 256 + GLUT_KEY_END: return ImGuiKey_End;
case 256 + GLUT_KEY_INSERT: return ImGuiKey_Insert;
case 127: return ImGuiKey_Delete;
case 8: return ImGuiKey_Backspace;
case ' ': return ImGuiKey_Space;
case 13: return ImGuiKey_Enter;
case 27: return ImGuiKey_Escape;
case 39: return ImGuiKey_Apostrophe;
case 44: return ImGuiKey_Comma;
case 45: return ImGuiKey_Minus;
case 46: return ImGuiKey_Period;
case 47: return ImGuiKey_Slash;
case 59: return ImGuiKey_Semicolon;
case 61: return ImGuiKey_Equal;
case 91: return ImGuiKey_LeftBracket;
case 92: return ImGuiKey_Backslash;
case 93: return ImGuiKey_RightBracket;
case 96: return ImGuiKey_GraveAccent;
//case 0: return ImGuiKey_CapsLock;
//case 0: return ImGuiKey_ScrollLock;
case 256 + 0x006D: return ImGuiKey_NumLock;
//case 0: return ImGuiKey_PrintScreen;
//case 0: return ImGuiKey_Pause;
//case '0': return ImGuiKey_Keypad0;
//case '1': return ImGuiKey_Keypad1;
//case '2': return ImGuiKey_Keypad2;
//case '3': return ImGuiKey_Keypad3;
//case '4': return ImGuiKey_Keypad4;
//case '5': return ImGuiKey_Keypad5;
//case '6': return ImGuiKey_Keypad6;
//case '7': return ImGuiKey_Keypad7;
//case '8': return ImGuiKey_Keypad8;
//case '9': return ImGuiKey_Keypad9;
//case 46: return ImGuiKey_KeypadDecimal;
//case 47: return ImGuiKey_KeypadDivide;
case 42: return ImGuiKey_KeypadMultiply;
//case 45: return ImGuiKey_KeypadSubtract;
case 43: return ImGuiKey_KeypadAdd;
//case 13: return ImGuiKey_KeypadEnter;
//case 0: return ImGuiKey_KeypadEqual;
case 256 + 0x0072: return ImGuiKey_LeftCtrl;
case 256 + 0x0070: return ImGuiKey_LeftShift;
case 256 + 0x0074: return ImGuiKey_LeftAlt;
//case 0: return ImGuiKey_LeftSuper;
case 256 + 0x0073: return ImGuiKey_RightCtrl;
case 256 + 0x0071: return ImGuiKey_RightShift;
case 256 + 0x0075: return ImGuiKey_RightAlt;
//case 0: return ImGuiKey_RightSuper;
//case 0: return ImGuiKey_Menu;
case '0': return ImGuiKey_0;
case '1': return ImGuiKey_1;
case '2': return ImGuiKey_2;
case '3': return ImGuiKey_3;
case '4': return ImGuiKey_4;
case '5': return ImGuiKey_5;
case '6': return ImGuiKey_6;
case '7': return ImGuiKey_7;
case '8': return ImGuiKey_8;
case '9': return ImGuiKey_9;
case 'A': case 'a': return ImGuiKey_A;
case 'B': case 'b': return ImGuiKey_B;
case 'C': case 'c': return ImGuiKey_C;
case 'D': case 'd': return ImGuiKey_D;
case 'E': case 'e': return ImGuiKey_E;
case 'F': case 'f': return ImGuiKey_F;
case 'G': case 'g': return ImGuiKey_G;
case 'H': case 'h': return ImGuiKey_H;
case 'I': case 'i': return ImGuiKey_I;
case 'J': case 'j': return ImGuiKey_J;
case 'K': case 'k': return ImGuiKey_K;
case 'L': case 'l': return ImGuiKey_L;
case 'M': case 'm': return ImGuiKey_M;
case 'N': case 'n': return ImGuiKey_N;
case 'O': case 'o': return ImGuiKey_O;
case 'P': case 'p': return ImGuiKey_P;
case 'Q': case 'q': return ImGuiKey_Q;
case 'R': case 'r': return ImGuiKey_R;
case 'S': case 's': return ImGuiKey_S;
case 'T': case 't': return ImGuiKey_T;
case 'U': case 'u': return ImGuiKey_U;
case 'V': case 'v': return ImGuiKey_V;
case 'W': case 'w': return ImGuiKey_W;
case 'X': case 'x': return ImGuiKey_X;
case 'Y': case 'y': return ImGuiKey_Y;
case 'Z': case 'z': return ImGuiKey_Z;
case 256 + GLUT_KEY_F1: return ImGuiKey_F1;
case 256 + GLUT_KEY_F2: return ImGuiKey_F2;
case 256 + GLUT_KEY_F3: return ImGuiKey_F3;
case 256 + GLUT_KEY_F4: return ImGuiKey_F4;
case 256 + GLUT_KEY_F5: return ImGuiKey_F5;
case 256 + GLUT_KEY_F6: return ImGuiKey_F6;
case 256 + GLUT_KEY_F7: return ImGuiKey_F7;
case 256 + GLUT_KEY_F8: return ImGuiKey_F8;
case 256 + GLUT_KEY_F9: return ImGuiKey_F9;
case 256 + GLUT_KEY_F10: return ImGuiKey_F10;
case 256 + GLUT_KEY_F11: return ImGuiKey_F11;
case 256 + GLUT_KEY_F12: return ImGuiKey_F12;
default: return ImGuiKey_None;
}
}
bool ImGui_ImplGLUT_Init()
{
ImGuiIO& io = ImGui::GetIO();
#ifdef FREEGLUT
io.BackendPlatformName = "imgui_impl_glut (freeglut)";
#else
io.BackendPlatformName = "imgui_impl_glut";
#endif
g_Time = 0;
return true;
}
void ImGui_ImplGLUT_InstallFuncs()
{
glutReshapeFunc(ImGui_ImplGLUT_ReshapeFunc);
glutMotionFunc(ImGui_ImplGLUT_MotionFunc);
glutPassiveMotionFunc(ImGui_ImplGLUT_MotionFunc);
glutMouseFunc(ImGui_ImplGLUT_MouseFunc);
#ifdef __FREEGLUT_EXT_H__
glutMouseWheelFunc(ImGui_ImplGLUT_MouseWheelFunc);
#endif
glutKeyboardFunc(ImGui_ImplGLUT_KeyboardFunc);
glutKeyboardUpFunc(ImGui_ImplGLUT_KeyboardUpFunc);
glutSpecialFunc(ImGui_ImplGLUT_SpecialFunc);
glutSpecialUpFunc(ImGui_ImplGLUT_SpecialUpFunc);
}
void ImGui_ImplGLUT_Shutdown()
{
}
void ImGui_ImplGLUT_NewFrame()
{
// Setup time step
ImGuiIO& io = ImGui::GetIO();
int current_time = glutGet(GLUT_ELAPSED_TIME);
int delta_time_ms = (current_time - g_Time);
if (delta_time_ms <= 0)
delta_time_ms = 1;
io.DeltaTime = delta_time_ms / 1000.0f;
g_Time = current_time;
// Start the frame
ImGui::NewFrame();
}
static void ImGui_ImplGLUT_UpdateKeyModifiers()
{
ImGuiIO& io = ImGui::GetIO();
int glut_key_mods = glutGetModifiers();
io.AddKeyEvent(ImGuiKey_ModCtrl, (glut_key_mods & GLUT_ACTIVE_CTRL) != 0);
io.AddKeyEvent(ImGuiKey_ModShift, (glut_key_mods & GLUT_ACTIVE_SHIFT) != 0);
io.AddKeyEvent(ImGuiKey_ModAlt, (glut_key_mods & GLUT_ACTIVE_ALT) != 0);
}
static void ImGui_ImplGLUT_AddKeyEvent(ImGuiKey key, bool down, int native_keycode)
{
ImGuiIO& io = ImGui::GetIO();
io.AddKeyEvent(key, down);
io.SetKeyEventNativeData(key, native_keycode, -1); // To support legacy indexing (<1.87 user code)
}
void ImGui_ImplGLUT_KeyboardFunc(unsigned char c, int x, int y)
{
// Send character to imgui
//printf("char_down_func %d '%c'\n", c, c);
ImGuiIO& io = ImGui::GetIO();
if (c >= 32)
io.AddInputCharacter((unsigned int)c);
ImGuiKey key = ImGui_ImplGLUT_KeyToImGuiKey(c);
ImGui_ImplGLUT_AddKeyEvent(key, true, c);
ImGui_ImplGLUT_UpdateKeyModifiers();
(void)x; (void)y; // Unused
}
void ImGui_ImplGLUT_KeyboardUpFunc(unsigned char c, int x, int y)
{
//printf("char_up_func %d '%c'\n", c, c);
ImGuiKey key = ImGui_ImplGLUT_KeyToImGuiKey(c);
ImGui_ImplGLUT_AddKeyEvent(key, false, c);
ImGui_ImplGLUT_UpdateKeyModifiers();
(void)x; (void)y; // Unused
}
void ImGui_ImplGLUT_SpecialFunc(int key, int x, int y)
{
//printf("key_down_func %d\n", key);
ImGuiKey imgui_key = ImGui_ImplGLUT_KeyToImGuiKey(key + 256);
ImGui_ImplGLUT_AddKeyEvent(imgui_key, true, key + 256);
ImGui_ImplGLUT_UpdateKeyModifiers();
(void)x; (void)y; // Unused
}
void ImGui_ImplGLUT_SpecialUpFunc(int key, int x, int y)
{
//printf("key_up_func %d\n", key);
ImGuiKey imgui_key = ImGui_ImplGLUT_KeyToImGuiKey(key + 256);
ImGui_ImplGLUT_AddKeyEvent(imgui_key, false, key + 256);
ImGui_ImplGLUT_UpdateKeyModifiers();
(void)x; (void)y; // Unused
}
void ImGui_ImplGLUT_MouseFunc(int glut_button, int state, int x, int y)
{
ImGuiIO& io = ImGui::GetIO();
io.AddMousePosEvent((float)x, (float)y);
int button = -1;
if (glut_button == GLUT_LEFT_BUTTON) button = 0;
if (glut_button == GLUT_RIGHT_BUTTON) button = 1;
if (glut_button == GLUT_MIDDLE_BUTTON) button = 2;
if (button != -1 && (state == GLUT_DOWN || state == GLUT_UP))
io.AddMouseButtonEvent(button, state == GLUT_DOWN);
}
#ifdef __FREEGLUT_EXT_H__
void ImGui_ImplGLUT_MouseWheelFunc(int button, int dir, int x, int y)
{
ImGuiIO& io = ImGui::GetIO();
io.AddMousePosEvent((float)x, (float)y);
if (dir != 0)
io.AddMouseWheelEvent(0.0f, dir > 0 ? 1.0f : -1.0f);
(void)button; // Unused
}
#endif
void ImGui_ImplGLUT_ReshapeFunc(int w, int h)
{
ImGuiIO& io = ImGui::GetIO();
io.DisplaySize = ImVec2((float)w, (float)h);
}
void ImGui_ImplGLUT_MotionFunc(int x, int y)
{
ImGuiIO& io = ImGui::GetIO();
io.AddMousePosEvent((float)x, (float)y);
}

View File

@ -1,39 +0,0 @@
// dear imgui: Platform Backend for GLUT/FreeGLUT
// This needs to be used along with a Renderer (e.g. OpenGL2)
// !!! GLUT/FreeGLUT IS OBSOLETE PREHISTORIC SOFTWARE. Using GLUT is not recommended unless you really miss the 90's. !!!
// !!! If someone or something is teaching you GLUT today, you are being abused. Please show some resistance. !!!
// !!! Nowadays, prefer using GLFW or SDL instead!
// Implemented features:
// [X] Platform: Partial keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLUT values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
// Issues:
// [ ] Platform: GLUT is unable to distinguish e.g. Backspace from CTRL+H or TAB from CTRL+I
// [ ] Platform: Missing mouse cursor shape/visibility support.
// [ ] Platform: Missing clipboard support (not supported by Glut).
// [ ] Platform: Missing gamepad support.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
IMGUI_IMPL_API bool ImGui_ImplGLUT_Init();
IMGUI_IMPL_API void ImGui_ImplGLUT_InstallFuncs();
IMGUI_IMPL_API void ImGui_ImplGLUT_Shutdown();
IMGUI_IMPL_API void ImGui_ImplGLUT_NewFrame();
// You can call ImGui_ImplGLUT_InstallFuncs() to get all those functions installed automatically,
// or call them yourself from your own GLUT handlers. We are using the same weird names as GLUT for consistency..
//---------------------------------------- GLUT name --------------------------------------------- Decent Name ---------
IMGUI_IMPL_API void ImGui_ImplGLUT_ReshapeFunc(int w, int h); // ~ ResizeFunc
IMGUI_IMPL_API void ImGui_ImplGLUT_MotionFunc(int x, int y); // ~ MouseMoveFunc
IMGUI_IMPL_API void ImGui_ImplGLUT_MouseFunc(int button, int state, int x, int y); // ~ MouseButtonFunc
IMGUI_IMPL_API void ImGui_ImplGLUT_MouseWheelFunc(int button, int dir, int x, int y); // ~ MouseWheelFunc
IMGUI_IMPL_API void ImGui_ImplGLUT_KeyboardFunc(unsigned char c, int x, int y); // ~ CharPressedFunc
IMGUI_IMPL_API void ImGui_ImplGLUT_KeyboardUpFunc(unsigned char c, int x, int y); // ~ CharReleasedFunc
IMGUI_IMPL_API void ImGui_ImplGLUT_SpecialFunc(int key, int x, int y); // ~ KeyPressedFunc
IMGUI_IMPL_API void ImGui_ImplGLUT_SpecialUpFunc(int key, int x, int y); // ~ KeyReleasedFunc

View File

@ -1,68 +0,0 @@
// dear imgui: Renderer Backend for Metal
// This needs to be used along with a Platform Backend (e.g. OSX)
// Implemented features:
// [X] Renderer: User texture binding. Use 'MTLTexture' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
#include "imgui.h" // IMGUI_IMPL_API
//-----------------------------------------------------------------------------
// ObjC API
//-----------------------------------------------------------------------------
#ifdef __OBJC__
@class MTLRenderPassDescriptor;
@protocol MTLDevice, MTLCommandBuffer, MTLRenderCommandEncoder;
IMGUI_IMPL_API bool ImGui_ImplMetal_Init(id<MTLDevice> device);
IMGUI_IMPL_API void ImGui_ImplMetal_Shutdown();
IMGUI_IMPL_API void ImGui_ImplMetal_NewFrame(MTLRenderPassDescriptor* renderPassDescriptor);
IMGUI_IMPL_API void ImGui_ImplMetal_RenderDrawData(ImDrawData* drawData,
id<MTLCommandBuffer> commandBuffer,
id<MTLRenderCommandEncoder> commandEncoder);
// Called by Init/NewFrame/Shutdown
IMGUI_IMPL_API bool ImGui_ImplMetal_CreateFontsTexture(id<MTLDevice> device);
IMGUI_IMPL_API void ImGui_ImplMetal_DestroyFontsTexture();
IMGUI_IMPL_API bool ImGui_ImplMetal_CreateDeviceObjects(id<MTLDevice> device);
IMGUI_IMPL_API void ImGui_ImplMetal_DestroyDeviceObjects();
#endif
//-----------------------------------------------------------------------------
// C++ API
//-----------------------------------------------------------------------------
// Enable Metal C++ binding support with '#define IMGUI_IMPL_METAL_CPP' in your imconfig.h file
// More info about using Metal from C++: https://developer.apple.com/metal/cpp/
#ifdef IMGUI_IMPL_METAL_CPP
#include <Metal/Metal.hpp>
#ifndef __OBJC__
IMGUI_IMPL_API bool ImGui_ImplMetal_Init(MTL::Device* device);
IMGUI_IMPL_API void ImGui_ImplMetal_Shutdown();
IMGUI_IMPL_API void ImGui_ImplMetal_NewFrame(MTL::RenderPassDescriptor* renderPassDescriptor);
IMGUI_IMPL_API void ImGui_ImplMetal_RenderDrawData(ImDrawData* draw_data,
MTL::CommandBuffer* commandBuffer,
MTL::RenderCommandEncoder* commandEncoder);
// Called by Init/NewFrame/Shutdown
IMGUI_IMPL_API bool ImGui_ImplMetal_CreateFontsTexture(MTL::Device* device);
IMGUI_IMPL_API void ImGui_ImplMetal_DestroyFontsTexture();
IMGUI_IMPL_API bool ImGui_ImplMetal_CreateDeviceObjects(MTL::Device* device);
IMGUI_IMPL_API void ImGui_ImplMetal_DestroyDeviceObjects();
#endif
#endif

View File

@ -1,726 +0,0 @@
// dear imgui: Renderer Backend for Metal
// This needs to be used along with a Platform Backend (e.g. OSX)
// Implemented features:
// [X] Renderer: User texture binding. Use 'MTLTexture' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2022-XX-XX: Metal: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2022-06-01: Metal: Fixed null dereference on exit inside command buffer completion handler.
// 2022-04-27: Misc: Store backend data in a per-context struct, allowing to use this backend with multiple contexts.
// 2022-01-03: Metal: Ignore ImDrawCmd where ElemCount == 0 (very rare but can technically be manufactured by user code).
// 2021-12-30: Metal: Added Metal C++ support. Enable with '#define IMGUI_IMPL_METAL_CPP' in your imconfig.h file.
// 2021-08-24: Metal: Fixed a crash when clipping rect larger than framebuffer is submitted. (#4464)
// 2021-05-19: Metal: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
// 2021-02-18: Metal: Change blending equation to preserve alpha in output buffer.
// 2021-01-25: Metal: Fixed texture storage mode when building on Mac Catalyst.
// 2019-05-29: Metal: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: Metal: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2019-02-11: Metal: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display.
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-07-05: Metal: Added new Metal backend implementation.
#include "imgui.h"
#include "imgui_impl_metal.h"
#import <time.h>
#import <Metal/Metal.h>
// Forward Declarations
static void ImGui_ImplMetal_InitPlatformInterface();
static void ImGui_ImplMetal_ShutdownPlatformInterface();
static void ImGui_ImplMetal_CreateDeviceObjectsForPlatformWindows();
static void ImGui_ImplMetal_InvalidateDeviceObjectsForPlatformWindows();
#pragma mark - Support classes
// A wrapper around a MTLBuffer object that knows the last time it was reused
@interface MetalBuffer : NSObject
@property (nonatomic, strong) id<MTLBuffer> buffer;
@property (nonatomic, assign) double lastReuseTime;
- (instancetype)initWithBuffer:(id<MTLBuffer>)buffer;
@end
// An object that encapsulates the data necessary to uniquely identify a
// render pipeline state. These are used as cache keys.
@interface FramebufferDescriptor : NSObject<NSCopying>
@property (nonatomic, assign) unsigned long sampleCount;
@property (nonatomic, assign) MTLPixelFormat colorPixelFormat;
@property (nonatomic, assign) MTLPixelFormat depthPixelFormat;
@property (nonatomic, assign) MTLPixelFormat stencilPixelFormat;
- (instancetype)initWithRenderPassDescriptor:(MTLRenderPassDescriptor*)renderPassDescriptor;
@end
// A singleton that stores long-lived objects that are needed by the Metal
// renderer backend. Stores the render pipeline state cache and the default
// font texture, and manages the reusable buffer cache.
@interface MetalContext : NSObject
@property (nonatomic, strong) id<MTLDevice> device;
@property (nonatomic, strong) id<MTLDepthStencilState> depthStencilState;
@property (nonatomic, strong) FramebufferDescriptor* framebufferDescriptor; // framebuffer descriptor for current frame; transient
@property (nonatomic, strong) NSMutableDictionary* renderPipelineStateCache; // pipeline cache; keyed on framebuffer descriptors
@property (nonatomic, strong, nullable) id<MTLTexture> fontTexture;
@property (nonatomic, strong) NSMutableArray<MetalBuffer*>* bufferCache;
@property (nonatomic, assign) double lastBufferCachePurge;
- (MetalBuffer*)dequeueReusableBufferOfLength:(NSUInteger)length device:(id<MTLDevice>)device;
- (id<MTLRenderPipelineState>)renderPipelineStateForFramebufferDescriptor:(FramebufferDescriptor*)descriptor device:(id<MTLDevice>)device;
@end
struct ImGui_ImplMetal_Data
{
MetalContext* SharedMetalContext;
ImGui_ImplMetal_Data() { memset(this, 0, sizeof(*this)); }
};
static ImGui_ImplMetal_Data* ImGui_ImplMetal_CreateBackendData() { return IM_NEW(ImGui_ImplMetal_Data)(); }
static ImGui_ImplMetal_Data* ImGui_ImplMetal_GetBackendData() { return ImGui::GetCurrentContext() ? (ImGui_ImplMetal_Data*)ImGui::GetIO().BackendRendererUserData : NULL; }
static void ImGui_ImplMetal_DestroyBackendData(){ IM_DELETE(ImGui_ImplMetal_GetBackendData()); }
static inline CFTimeInterval GetMachAbsoluteTimeInSeconds() { return (CFTimeInterval)(double)(clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1e9); }
#ifdef IMGUI_IMPL_METAL_CPP
#pragma mark - Dear ImGui Metal C++ Backend API
bool ImGui_ImplMetal_Init(MTL::Device* device)
{
return ImGui_ImplMetal_Init((id<MTLDevice>)(device));
}
void ImGui_ImplMetal_NewFrame(MTL::RenderPassDescriptor* renderPassDescriptor)
{
ImGui_ImplMetal_NewFrame((MTLRenderPassDescriptor*)(renderPassDescriptor));
}
void ImGui_ImplMetal_RenderDrawData(ImDrawData* draw_data,
MTL::CommandBuffer* commandBuffer,
MTL::RenderCommandEncoder* commandEncoder)
{
ImGui_ImplMetal_RenderDrawData(draw_data,
(id<MTLCommandBuffer>)(commandBuffer),
(id<MTLRenderCommandEncoder>)(commandEncoder));
}
bool ImGui_ImplMetal_CreateFontsTexture(MTL::Device* device)
{
return ImGui_ImplMetal_CreateFontsTexture((id<MTLDevice>)(device));
}
bool ImGui_ImplMetal_CreateDeviceObjects(MTL::Device* device)
{
return ImGui_ImplMetal_CreateDeviceObjects((id<MTLDevice>)(device));
}
#endif // #ifdef IMGUI_IMPL_METAL_CPP
#pragma mark - Dear ImGui Metal Backend API
bool ImGui_ImplMetal_Init(id<MTLDevice> device)
{
ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_CreateBackendData();
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererUserData = (void*)bd;
io.BackendRendererName = "imgui_impl_metal";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional)
bd->SharedMetalContext = [[MetalContext alloc] init];
bd->SharedMetalContext.device = device;
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
ImGui_ImplMetal_InitPlatformInterface();
return true;
}
void ImGui_ImplMetal_Shutdown()
{
ImGui_ImplMetal_ShutdownPlatformInterface();
ImGui_ImplMetal_DestroyDeviceObjects();
ImGui_ImplMetal_DestroyBackendData();
}
void ImGui_ImplMetal_NewFrame(MTLRenderPassDescriptor* renderPassDescriptor)
{
ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData();
IM_ASSERT(bd->SharedMetalContext != nil && "No Metal context. Did you call ImGui_ImplMetal_Init() ?");
bd->SharedMetalContext.framebufferDescriptor = [[FramebufferDescriptor alloc] initWithRenderPassDescriptor:renderPassDescriptor];
if (bd->SharedMetalContext.depthStencilState == nil)
ImGui_ImplMetal_CreateDeviceObjects(bd->SharedMetalContext.device);
}
static void ImGui_ImplMetal_SetupRenderState(ImDrawData* drawData, id<MTLCommandBuffer> commandBuffer,
id<MTLRenderCommandEncoder> commandEncoder, id<MTLRenderPipelineState> renderPipelineState,
MetalBuffer* vertexBuffer, size_t vertexBufferOffset)
{
IM_UNUSED(commandBuffer);
ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData();
[commandEncoder setCullMode:MTLCullModeNone];
[commandEncoder setDepthStencilState:bd->SharedMetalContext.depthStencilState];
// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to
// draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps.
MTLViewport viewport =
{
.originX = 0.0,
.originY = 0.0,
.width = (double)(drawData->DisplaySize.x * drawData->FramebufferScale.x),
.height = (double)(drawData->DisplaySize.y * drawData->FramebufferScale.y),
.znear = 0.0,
.zfar = 1.0
};
[commandEncoder setViewport:viewport];
float L = drawData->DisplayPos.x;
float R = drawData->DisplayPos.x + drawData->DisplaySize.x;
float T = drawData->DisplayPos.y;
float B = drawData->DisplayPos.y + drawData->DisplaySize.y;
float N = (float)viewport.znear;
float F = (float)viewport.zfar;
const float ortho_projection[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, 1/(F-N), 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), N/(F-N), 1.0f },
};
[commandEncoder setVertexBytes:&ortho_projection length:sizeof(ortho_projection) atIndex:1];
[commandEncoder setRenderPipelineState:renderPipelineState];
[commandEncoder setVertexBuffer:vertexBuffer.buffer offset:0 atIndex:0];
[commandEncoder setVertexBufferOffset:vertexBufferOffset atIndex:0];
}
// Metal Render function.
void ImGui_ImplMetal_RenderDrawData(ImDrawData* drawData, id<MTLCommandBuffer> commandBuffer, id<MTLRenderCommandEncoder> commandEncoder)
{
ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData();
MetalContext* ctx = bd->SharedMetalContext;
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
int fb_width = (int)(drawData->DisplaySize.x * drawData->FramebufferScale.x);
int fb_height = (int)(drawData->DisplaySize.y * drawData->FramebufferScale.y);
if (fb_width <= 0 || fb_height <= 0 || drawData->CmdListsCount == 0)
return;
// Try to retrieve a render pipeline state that is compatible with the framebuffer config for this frame
// The hit rate for this cache should be very near 100%.
id<MTLRenderPipelineState> renderPipelineState = ctx.renderPipelineStateCache[ctx.framebufferDescriptor];
if (renderPipelineState == nil)
{
// No luck; make a new render pipeline state
renderPipelineState = [ctx renderPipelineStateForFramebufferDescriptor:ctx.framebufferDescriptor device:commandBuffer.device];
// Cache render pipeline state for later reuse
ctx.renderPipelineStateCache[ctx.framebufferDescriptor] = renderPipelineState;
}
size_t vertexBufferLength = (size_t)drawData->TotalVtxCount * sizeof(ImDrawVert);
size_t indexBufferLength = (size_t)drawData->TotalIdxCount * sizeof(ImDrawIdx);
MetalBuffer* vertexBuffer = [ctx dequeueReusableBufferOfLength:vertexBufferLength device:commandBuffer.device];
MetalBuffer* indexBuffer = [ctx dequeueReusableBufferOfLength:indexBufferLength device:commandBuffer.device];
ImGui_ImplMetal_SetupRenderState(drawData, commandBuffer, commandEncoder, renderPipelineState, vertexBuffer, 0);
// Will project scissor/clipping rectangles into framebuffer space
ImVec2 clip_off = drawData->DisplayPos; // (0,0) unless using multi-viewports
ImVec2 clip_scale = drawData->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
// Render command lists
size_t vertexBufferOffset = 0;
size_t indexBufferOffset = 0;
for (int n = 0; n < drawData->CmdListsCount; n++)
{
const ImDrawList* cmd_list = drawData->CmdLists[n];
memcpy((char*)vertexBuffer.buffer.contents + vertexBufferOffset, cmd_list->VtxBuffer.Data, (size_t)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
memcpy((char*)indexBuffer.buffer.contents + indexBufferOffset, cmd_list->IdxBuffer.Data, (size_t)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplMetal_SetupRenderState(drawData, commandBuffer, commandEncoder, renderPipelineState, vertexBuffer, vertexBufferOffset);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
// Clamp to viewport as setScissorRect() won't accept values that are off bounds
if (clip_min.x < 0.0f) { clip_min.x = 0.0f; }
if (clip_min.y < 0.0f) { clip_min.y = 0.0f; }
if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; }
if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; }
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
continue;
if (pcmd->ElemCount == 0) // drawIndexedPrimitives() validation doesn't accept this
continue;
// Apply scissor/clipping rectangle
MTLScissorRect scissorRect =
{
.x = NSUInteger(clip_min.x),
.y = NSUInteger(clip_min.y),
.width = NSUInteger(clip_max.x - clip_min.x),
.height = NSUInteger(clip_max.y - clip_min.y)
};
[commandEncoder setScissorRect:scissorRect];
// Bind texture, Draw
if (ImTextureID tex_id = pcmd->GetTexID())
[commandEncoder setFragmentTexture:(__bridge id<MTLTexture>)(tex_id) atIndex:0];
[commandEncoder setVertexBufferOffset:(vertexBufferOffset + pcmd->VtxOffset * sizeof(ImDrawVert)) atIndex:0];
[commandEncoder drawIndexedPrimitives:MTLPrimitiveTypeTriangle
indexCount:pcmd->ElemCount
indexType:sizeof(ImDrawIdx) == 2 ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32
indexBuffer:indexBuffer.buffer
indexBufferOffset:indexBufferOffset + pcmd->IdxOffset * sizeof(ImDrawIdx)];
}
}
vertexBufferOffset += (size_t)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert);
indexBufferOffset += (size_t)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx);
}
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer>)
{
dispatch_async(dispatch_get_main_queue(), ^{
ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData();
if (bd != NULL)
{
[bd->SharedMetalContext.bufferCache addObject:vertexBuffer];
[bd->SharedMetalContext.bufferCache addObject:indexBuffer];
}
});
}];
}
bool ImGui_ImplMetal_CreateFontsTexture(id<MTLDevice> device)
{
ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData();
ImGuiIO& io = ImGui::GetIO();
// We are retrieving and uploading the font atlas as a 4-channels RGBA texture here.
// In theory we could call GetTexDataAsAlpha8() and upload a 1-channel texture to save on memory access bandwidth.
// However, using a shader designed for 1-channel texture would make it less obvious to use the ImTextureID facility to render users own textures.
// You can make that change in your implementation.
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
MTLTextureDescriptor* textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm
width:(NSUInteger)width
height:(NSUInteger)height
mipmapped:NO];
textureDescriptor.usage = MTLTextureUsageShaderRead;
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
textureDescriptor.storageMode = MTLStorageModeManaged;
#else
textureDescriptor.storageMode = MTLStorageModeShared;
#endif
id <MTLTexture> texture = [device newTextureWithDescriptor:textureDescriptor];
[texture replaceRegion:MTLRegionMake2D(0, 0, (NSUInteger)width, (NSUInteger)height) mipmapLevel:0 withBytes:pixels bytesPerRow:(NSUInteger)width * 4];
bd->SharedMetalContext.fontTexture = texture;
io.Fonts->SetTexID((__bridge void*)bd->SharedMetalContext.fontTexture); // ImTextureID == void*
return (bd->SharedMetalContext.fontTexture != nil);
}
void ImGui_ImplMetal_DestroyFontsTexture()
{
ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData();
ImGuiIO& io = ImGui::GetIO();
bd->SharedMetalContext.fontTexture = nil;
io.Fonts->SetTexID(nullptr);
}
bool ImGui_ImplMetal_CreateDeviceObjects(id<MTLDevice> device)
{
ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData();
MTLDepthStencilDescriptor* depthStencilDescriptor = [[MTLDepthStencilDescriptor alloc] init];
depthStencilDescriptor.depthWriteEnabled = NO;
depthStencilDescriptor.depthCompareFunction = MTLCompareFunctionAlways;
bd->SharedMetalContext.depthStencilState = [device newDepthStencilStateWithDescriptor:depthStencilDescriptor];
ImGui_ImplMetal_CreateDeviceObjectsForPlatformWindows();
ImGui_ImplMetal_CreateFontsTexture(device);
return true;
}
void ImGui_ImplMetal_DestroyDeviceObjects()
{
ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData();
ImGui_ImplMetal_DestroyFontsTexture();
ImGui_ImplMetal_InvalidateDeviceObjectsForPlatformWindows();
[bd->SharedMetalContext.renderPipelineStateCache removeAllObjects];
}
#pragma mark - Multi-viewport support
#import <QuartzCore/CAMetalLayer.h>
#if TARGET_OS_OSX
#import <Cocoa/Cocoa.h>
#endif
//--------------------------------------------------------------------------------------------------------
// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
// This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously.
// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
//--------------------------------------------------------------------------------------------------------
struct ImGuiViewportDataMetal
{
CAMetalLayer* MetalLayer;
id<MTLCommandQueue> CommandQueue;
MTLRenderPassDescriptor* RenderPassDescriptor;
void* Handle = NULL;
bool FirstFrame = true;
};
static void ImGui_ImplMetal_CreateWindow(ImGuiViewport* viewport)
{
ImGui_ImplMetal_Data* bd = ImGui_ImplMetal_GetBackendData();
ImGuiViewportDataMetal* data = IM_NEW(ImGuiViewportDataMetal)();
viewport->RendererUserData = data;
// PlatformHandleRaw should always be a NSWindow*, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*).
// Some back-ends will leave PlatformHandleRaw NULL, in which case we assume PlatformHandle will contain the NSWindow*.
void* handle = viewport->PlatformHandleRaw ? viewport->PlatformHandleRaw : viewport->PlatformHandle;
IM_ASSERT(handle != NULL);
id<MTLDevice> device = [bd->SharedMetalContext.depthStencilState device];
CAMetalLayer* layer = [CAMetalLayer layer];
layer.device = device;
layer.framebufferOnly = YES;
layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
#if TARGET_OS_OSX
NSWindow* window = (__bridge NSWindow*)handle;
NSView* view = window.contentView;
view.layer = layer;
view.wantsLayer = YES;
#endif
data->MetalLayer = layer;
data->CommandQueue = [device newCommandQueue];
data->RenderPassDescriptor = [[MTLRenderPassDescriptor alloc] init];
data->Handle = handle;
}
static void ImGui_ImplMetal_DestroyWindow(ImGuiViewport* viewport)
{
// The main viewport (owned by the application) will always have RendererUserData == NULL since we didn't create the data for it.
if (ImGuiViewportDataMetal* data = (ImGuiViewportDataMetal*)viewport->RendererUserData)
IM_DELETE(data);
viewport->RendererUserData = NULL;
}
inline static CGSize MakeScaledSize(CGSize size, CGFloat scale)
{
return CGSizeMake(size.width * scale, size.height * scale);
}
static void ImGui_ImplMetal_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
{
ImGuiViewportDataMetal* data = (ImGuiViewportDataMetal*)viewport->RendererUserData;
data->MetalLayer.drawableSize = MakeScaledSize(CGSizeMake(size.x, size.y), viewport->DpiScale);
}
static void ImGui_ImplMetal_RenderWindow(ImGuiViewport* viewport, void*)
{
ImGuiViewportDataMetal* data = (ImGuiViewportDataMetal*)viewport->RendererUserData;
#if TARGET_OS_OSX
void* handle = viewport->PlatformHandleRaw ? viewport->PlatformHandleRaw : viewport->PlatformHandle;
NSWindow* window = (__bridge NSWindow*)handle;
// Always render the first frame, regardless of occlusionState, to avoid an initial flicker
if ((window.occlusionState & NSWindowOcclusionStateVisible) == 0 && !data->FirstFrame)
{
// Do not render windows which are completely occluded. Calling -[CAMetalLayer nextDrawable] will hang for
// approximately 1 second if the Metal layer is completely occluded.
return;
}
data->FirstFrame = false;
viewport->DpiScale = (float)window.backingScaleFactor;
if (data->MetalLayer.contentsScale != viewport->DpiScale)
{
data->MetalLayer.contentsScale = viewport->DpiScale;
data->MetalLayer.drawableSize = MakeScaledSize(window.frame.size, viewport->DpiScale);
}
viewport->DrawData->FramebufferScale = ImVec2(viewport->DpiScale, viewport->DpiScale);
#endif
id <CAMetalDrawable> drawable = [data->MetalLayer nextDrawable];
if (drawable == nil)
return;
MTLRenderPassDescriptor* renderPassDescriptor = data->RenderPassDescriptor;
renderPassDescriptor.colorAttachments[0].texture = drawable.texture;
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 0);
if ((viewport->Flags & ImGuiViewportFlags_NoRendererClear) == 0)
renderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
id <MTLCommandBuffer> commandBuffer = [data->CommandQueue commandBuffer];
id <MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
ImGui_ImplMetal_RenderDrawData(viewport->DrawData, commandBuffer, renderEncoder);
[renderEncoder endEncoding];
[commandBuffer presentDrawable:drawable];
[commandBuffer commit];
}
static void ImGui_ImplMetal_InitPlatformInterface()
{
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
platform_io.Renderer_CreateWindow = ImGui_ImplMetal_CreateWindow;
platform_io.Renderer_DestroyWindow = ImGui_ImplMetal_DestroyWindow;
platform_io.Renderer_SetWindowSize = ImGui_ImplMetal_SetWindowSize;
platform_io.Renderer_RenderWindow = ImGui_ImplMetal_RenderWindow;
}
static void ImGui_ImplMetal_ShutdownPlatformInterface()
{
ImGui::DestroyPlatformWindows();
}
static void ImGui_ImplMetal_CreateDeviceObjectsForPlatformWindows()
{
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
for (int i = 1; i < platform_io.Viewports.Size; i++)
if (!platform_io.Viewports[i]->RendererUserData)
ImGui_ImplMetal_CreateWindow(platform_io.Viewports[i]);
}
static void ImGui_ImplMetal_InvalidateDeviceObjectsForPlatformWindows()
{
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
for (int i = 1; i < platform_io.Viewports.Size; i++)
if (platform_io.Viewports[i]->RendererUserData)
ImGui_ImplMetal_DestroyWindow(platform_io.Viewports[i]);
}
#pragma mark - MetalBuffer implementation
@implementation MetalBuffer
- (instancetype)initWithBuffer:(id<MTLBuffer>)buffer
{
if ((self = [super init]))
{
_buffer = buffer;
_lastReuseTime = GetMachAbsoluteTimeInSeconds();
}
return self;
}
@end
#pragma mark - FramebufferDescriptor implementation
@implementation FramebufferDescriptor
- (instancetype)initWithRenderPassDescriptor:(MTLRenderPassDescriptor*)renderPassDescriptor
{
if ((self = [super init]))
{
_sampleCount = renderPassDescriptor.colorAttachments[0].texture.sampleCount;
_colorPixelFormat = renderPassDescriptor.colorAttachments[0].texture.pixelFormat;
_depthPixelFormat = renderPassDescriptor.depthAttachment.texture.pixelFormat;
_stencilPixelFormat = renderPassDescriptor.stencilAttachment.texture.pixelFormat;
}
return self;
}
- (nonnull id)copyWithZone:(nullable NSZone*)zone
{
FramebufferDescriptor* copy = [[FramebufferDescriptor allocWithZone:zone] init];
copy.sampleCount = self.sampleCount;
copy.colorPixelFormat = self.colorPixelFormat;
copy.depthPixelFormat = self.depthPixelFormat;
copy.stencilPixelFormat = self.stencilPixelFormat;
return copy;
}
- (NSUInteger)hash
{
NSUInteger sc = _sampleCount & 0x3;
NSUInteger cf = _colorPixelFormat & 0x3FF;
NSUInteger df = _depthPixelFormat & 0x3FF;
NSUInteger sf = _stencilPixelFormat & 0x3FF;
NSUInteger hash = (sf << 22) | (df << 12) | (cf << 2) | sc;
return hash;
}
- (BOOL)isEqual:(id)object
{
FramebufferDescriptor* other = object;
if (![other isKindOfClass:[FramebufferDescriptor class]])
return NO;
return other.sampleCount == self.sampleCount &&
other.colorPixelFormat == self.colorPixelFormat &&
other.depthPixelFormat == self.depthPixelFormat &&
other.stencilPixelFormat == self.stencilPixelFormat;
}
@end
#pragma mark - MetalContext implementation
@implementation MetalContext
- (instancetype)init
{
if ((self = [super init]))
{
_renderPipelineStateCache = [NSMutableDictionary dictionary];
_bufferCache = [NSMutableArray array];
_lastBufferCachePurge = GetMachAbsoluteTimeInSeconds();
}
return self;
}
- (MetalBuffer*)dequeueReusableBufferOfLength:(NSUInteger)length device:(id<MTLDevice>)device
{
uint64_t now = GetMachAbsoluteTimeInSeconds();
// Purge old buffers that haven't been useful for a while
if (now - self.lastBufferCachePurge > 1.0)
{
NSMutableArray* survivors = [NSMutableArray array];
for (MetalBuffer* candidate in self.bufferCache)
if (candidate.lastReuseTime > self.lastBufferCachePurge)
[survivors addObject:candidate];
self.bufferCache = [survivors mutableCopy];
self.lastBufferCachePurge = now;
}
// See if we have a buffer we can reuse
MetalBuffer* bestCandidate = nil;
for (MetalBuffer* candidate in self.bufferCache)
if (candidate.buffer.length >= length && (bestCandidate == nil || bestCandidate.lastReuseTime > candidate.lastReuseTime))
bestCandidate = candidate;
if (bestCandidate != nil)
{
[self.bufferCache removeObject:bestCandidate];
bestCandidate.lastReuseTime = now;
return bestCandidate;
}
// No luck; make a new buffer
id<MTLBuffer> backing = [device newBufferWithLength:length options:MTLResourceStorageModeShared];
return [[MetalBuffer alloc] initWithBuffer:backing];
}
// Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling.
- (id<MTLRenderPipelineState>)renderPipelineStateForFramebufferDescriptor:(FramebufferDescriptor*)descriptor device:(id<MTLDevice>)device
{
NSError* error = nil;
NSString* shaderSource = @""
"#include <metal_stdlib>\n"
"using namespace metal;\n"
"\n"
"struct Uniforms {\n"
" float4x4 projectionMatrix;\n"
"};\n"
"\n"
"struct VertexIn {\n"
" float2 position [[attribute(0)]];\n"
" float2 texCoords [[attribute(1)]];\n"
" uchar4 color [[attribute(2)]];\n"
"};\n"
"\n"
"struct VertexOut {\n"
" float4 position [[position]];\n"
" float2 texCoords;\n"
" float4 color;\n"
"};\n"
"\n"
"vertex VertexOut vertex_main(VertexIn in [[stage_in]],\n"
" constant Uniforms &uniforms [[buffer(1)]]) {\n"
" VertexOut out;\n"
" out.position = uniforms.projectionMatrix * float4(in.position, 0, 1);\n"
" out.texCoords = in.texCoords;\n"
" out.color = float4(in.color) / float4(255.0);\n"
" return out;\n"
"}\n"
"\n"
"fragment half4 fragment_main(VertexOut in [[stage_in]],\n"
" texture2d<half, access::sample> texture [[texture(0)]]) {\n"
" constexpr sampler linearSampler(coord::normalized, min_filter::linear, mag_filter::linear, mip_filter::linear);\n"
" half4 texColor = texture.sample(linearSampler, in.texCoords);\n"
" return half4(in.color) * texColor;\n"
"}\n";
id<MTLLibrary> library = [device newLibraryWithSource:shaderSource options:nil error:&error];
if (library == nil)
{
NSLog(@"Error: failed to create Metal library: %@", error);
return nil;
}
id<MTLFunction> vertexFunction = [library newFunctionWithName:@"vertex_main"];
id<MTLFunction> fragmentFunction = [library newFunctionWithName:@"fragment_main"];
if (vertexFunction == nil || fragmentFunction == nil)
{
NSLog(@"Error: failed to find Metal shader functions in library: %@", error);
return nil;
}
MTLVertexDescriptor* vertexDescriptor = [MTLVertexDescriptor vertexDescriptor];
vertexDescriptor.attributes[0].offset = IM_OFFSETOF(ImDrawVert, pos);
vertexDescriptor.attributes[0].format = MTLVertexFormatFloat2; // position
vertexDescriptor.attributes[0].bufferIndex = 0;
vertexDescriptor.attributes[1].offset = IM_OFFSETOF(ImDrawVert, uv);
vertexDescriptor.attributes[1].format = MTLVertexFormatFloat2; // texCoords
vertexDescriptor.attributes[1].bufferIndex = 0;
vertexDescriptor.attributes[2].offset = IM_OFFSETOF(ImDrawVert, col);
vertexDescriptor.attributes[2].format = MTLVertexFormatUChar4; // color
vertexDescriptor.attributes[2].bufferIndex = 0;
vertexDescriptor.layouts[0].stepRate = 1;
vertexDescriptor.layouts[0].stepFunction = MTLVertexStepFunctionPerVertex;
vertexDescriptor.layouts[0].stride = sizeof(ImDrawVert);
MTLRenderPipelineDescriptor* pipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
pipelineDescriptor.vertexFunction = vertexFunction;
pipelineDescriptor.fragmentFunction = fragmentFunction;
pipelineDescriptor.vertexDescriptor = vertexDescriptor;
pipelineDescriptor.sampleCount = self.framebufferDescriptor.sampleCount;
pipelineDescriptor.colorAttachments[0].pixelFormat = self.framebufferDescriptor.colorPixelFormat;
pipelineDescriptor.colorAttachments[0].blendingEnabled = YES;
pipelineDescriptor.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;
pipelineDescriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha;
pipelineDescriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
pipelineDescriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;
pipelineDescriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne;
pipelineDescriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;
pipelineDescriptor.depthAttachmentPixelFormat = self.framebufferDescriptor.depthPixelFormat;
pipelineDescriptor.stencilAttachmentPixelFormat = self.framebufferDescriptor.stencilPixelFormat;
id<MTLRenderPipelineState> renderPipelineState = [device newRenderPipelineStateWithDescriptor:pipelineDescriptor error:&error];
if (error != nil)
NSLog(@"Error: failed to create Metal pipeline state: %@", error);
return renderPipelineState;
}
@end

View File

@ -1,325 +0,0 @@
// dear imgui: Renderer Backend for OpenGL2 (legacy OpenGL, fixed pipeline)
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)**
// **Prefer using the code in imgui_impl_opengl3.cpp**
// This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read.
// If your code is using GL3+ context or any semi modern OpenGL calls, using this is likely to make everything more
// complicated, will require your code to reset every single OpenGL attributes to their initial state, and might
// confuse your GPU driver.
// The GL2 code is unable to reset attributes or even call e.g. "glUseProgram(0)" because they don't exist in that API.
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2022-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2021-12-08: OpenGL: Fixed mishandling of the the ImDrawCmd::IdxOffset field! This is an old bug but it never had an effect until some internal rendering changes in 1.86.
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
// 2021-05-19: OpenGL: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
// 2021-01-03: OpenGL: Backup, setup and restore GL_SHADE_MODEL state, disable GL_STENCIL_TEST and disable GL_NORMAL_ARRAY client state to increase compatibility with legacy OpenGL applications.
// 2020-01-23: OpenGL: Backup, setup and restore GL_TEXTURE_ENV to increase compatibility with legacy OpenGL applications.
// 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display.
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-08-03: OpenGL: Disabling/restoring GL_LIGHTING and GL_COLOR_MATERIAL to increase compatibility with legacy OpenGL applications.
// 2018-06-08: Misc: Extracted imgui_impl_opengl2.cpp/.h away from the old combined GLFW/SDL+OpenGL2 examples.
// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplOpenGL2_RenderDrawData() in the .h file so you can call it yourself.
// 2017-09-01: OpenGL: Save and restore current polygon mode.
// 2016-09-10: OpenGL: Uploading font texture as RGBA32 to increase compatibility with users shaders (not ideal).
// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle.
#include "imgui.h"
#include "imgui_impl_opengl2.h"
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
#include <stddef.h> // intptr_t
#else
#include <stdint.h> // intptr_t
#endif
// Include OpenGL header (without an OpenGL loader) requires a bit of fiddling
#if defined(_WIN32) && !defined(APIENTRY)
#define APIENTRY __stdcall // It is customary to use APIENTRY for OpenGL function pointer declarations on all platforms. Additionally, the Windows OpenGL header needs APIENTRY.
#endif
#if defined(_WIN32) && !defined(WINGDIAPI)
#define WINGDIAPI __declspec(dllimport) // Some Windows OpenGL headers need this
#endif
#if defined(__APPLE__)
#define GL_SILENCE_DEPRECATION
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
struct ImGui_ImplOpenGL2_Data
{
GLuint FontTexture;
ImGui_ImplOpenGL2_Data() { memset((void*)this, 0, sizeof(*this)); }
};
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
static ImGui_ImplOpenGL2_Data* ImGui_ImplOpenGL2_GetBackendData()
{
return ImGui::GetCurrentContext() ? (ImGui_ImplOpenGL2_Data*)ImGui::GetIO().BackendRendererUserData : NULL;
}
// Forward Declarations
static void ImGui_ImplOpenGL2_InitPlatformInterface();
static void ImGui_ImplOpenGL2_ShutdownPlatformInterface();
// Functions
bool ImGui_ImplOpenGL2_Init()
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!");
// Setup backend capabilities flags
ImGui_ImplOpenGL2_Data* bd = IM_NEW(ImGui_ImplOpenGL2_Data)();
io.BackendRendererUserData = (void*)bd;
io.BackendRendererName = "imgui_impl_opengl2";
io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional)
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
ImGui_ImplOpenGL2_InitPlatformInterface();
return true;
}
void ImGui_ImplOpenGL2_Shutdown()
{
ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData();
IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplOpenGL2_ShutdownPlatformInterface();
ImGui_ImplOpenGL2_DestroyDeviceObjects();
io.BackendRendererName = NULL;
io.BackendRendererUserData = NULL;
IM_DELETE(bd);
}
void ImGui_ImplOpenGL2_NewFrame()
{
ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData();
IM_ASSERT(bd != NULL && "Did you call ImGui_ImplOpenGL2_Init()?");
if (!bd->FontTexture)
ImGui_ImplOpenGL2_CreateDeviceObjects();
}
static void ImGui_ImplOpenGL2_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height)
{
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers, polygon fill.
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // In order to composite our output buffer we need to preserve alpha
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glDisable(GL_LIGHTING);
glDisable(GL_COLOR_MATERIAL);
glEnable(GL_SCISSOR_TEST);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glEnable(GL_TEXTURE_2D);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glShadeModel(GL_SMOOTH);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
// If you are using this code with non-legacy OpenGL header/contexts (which you should not, prefer using imgui_impl_opengl3.cpp!!),
// you may need to backup/reset/restore other state, e.g. for current shader using the commented lines below.
// (DO NOT MODIFY THIS FILE! Add the code in your calling function)
// GLint last_program;
// glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
// glUseProgram(0);
// ImGui_ImplOpenGL2_RenderDrawData(...);
// glUseProgram(last_program)
// There are potentially many more states you could need to clear/setup that we can't access from default headers.
// e.g. glBindBuffer(GL_ARRAY_BUFFER, 0), glDisable(GL_TEXTURE_CUBE_MAP).
// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(draw_data->DisplayPos.x, draw_data->DisplayPos.x + draw_data->DisplaySize.x, draw_data->DisplayPos.y + draw_data->DisplaySize.y, draw_data->DisplayPos.y, -1.0f, +1.0f);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
}
// OpenGL2 Render function.
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly.
// This is in order to be able to run within an OpenGL engine that doesn't do so.
void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
if (fb_width == 0 || fb_height == 0)
return;
// Backup GL state
GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);
GLint last_shade_model; glGetIntegerv(GL_SHADE_MODEL, &last_shade_model);
GLint last_tex_env_mode; glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &last_tex_env_mode);
glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT);
// Setup desired GL state
ImGui_ImplOpenGL2_SetupRenderState(draw_data, fb_width, fb_height);
// Will project scissor/clipping rectangles into framebuffer space
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
// Render command lists
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data;
const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data;
glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, pos)));
glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv)));
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col)));
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplOpenGL2_SetupRenderState(draw_data, fb_width, fb_height);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
continue;
// Apply scissor/clipping rectangle (Y is inverted in OpenGL)
glScissor((int)clip_min.x, (int)(fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y));
// Bind texture, Draw
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID());
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer + pcmd->IdxOffset);
}
}
}
// Restore modified GL state
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glBindTexture(GL_TEXTURE_2D, (GLuint)last_texture);
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glPopAttrib();
glPolygonMode(GL_FRONT, (GLenum)last_polygon_mode[0]); glPolygonMode(GL_BACK, (GLenum)last_polygon_mode[1]);
glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
glShadeModel(last_shade_model);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, last_tex_env_mode);
}
bool ImGui_ImplOpenGL2_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
// Upload texture to graphics system
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
GLint last_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGenTextures(1, &bd->FontTexture);
glBindTexture(GL_TEXTURE_2D, bd->FontTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Store our identifier
io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture);
// Restore state
glBindTexture(GL_TEXTURE_2D, last_texture);
return true;
}
void ImGui_ImplOpenGL2_DestroyFontsTexture()
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplOpenGL2_Data* bd = ImGui_ImplOpenGL2_GetBackendData();
if (bd->FontTexture)
{
glDeleteTextures(1, &bd->FontTexture);
io.Fonts->SetTexID(0);
bd->FontTexture = 0;
}
}
bool ImGui_ImplOpenGL2_CreateDeviceObjects()
{
return ImGui_ImplOpenGL2_CreateFontsTexture();
}
void ImGui_ImplOpenGL2_DestroyDeviceObjects()
{
ImGui_ImplOpenGL2_DestroyFontsTexture();
}
//--------------------------------------------------------------------------------------------------------
// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
//--------------------------------------------------------------------------------------------------------
static void ImGui_ImplOpenGL2_RenderWindow(ImGuiViewport* viewport, void*)
{
if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear))
{
ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w);
glClear(GL_COLOR_BUFFER_BIT);
}
ImGui_ImplOpenGL2_RenderDrawData(viewport->DrawData);
}
static void ImGui_ImplOpenGL2_InitPlatformInterface()
{
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
platform_io.Renderer_RenderWindow = ImGui_ImplOpenGL2_RenderWindow;
}
static void ImGui_ImplOpenGL2_ShutdownPlatformInterface()
{
ImGui::DestroyPlatformWindows();
}

View File

@ -1,33 +0,0 @@
// dear imgui: Renderer Backend for OpenGL2 (legacy OpenGL, fixed pipeline)
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)**
// **Prefer using the code in imgui_impl_opengl3.cpp**
// This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read.
// If your code is using GL3+ context or any semi modern OpenGL calls, using this is likely to make everything more
// complicated, will require your code to reset every single OpenGL attributes to their initial state, and might
// confuse your GPU driver.
// The GL2 code is unable to reset attributes or even call e.g. "glUseProgram(0)" because they don't exist in that API.
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
IMGUI_IMPL_API bool ImGui_ImplOpenGL2_Init();
IMGUI_IMPL_API void ImGui_ImplOpenGL2_Shutdown();
IMGUI_IMPL_API void ImGui_ImplOpenGL2_NewFrame();
IMGUI_IMPL_API void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data);
// Called by Init/NewFrame/Shutdown
IMGUI_IMPL_API bool ImGui_ImplOpenGL2_CreateFontsTexture();
IMGUI_IMPL_API void ImGui_ImplOpenGL2_DestroyFontsTexture();
IMGUI_IMPL_API bool ImGui_ImplOpenGL2_CreateDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplOpenGL2_DestroyDeviceObjects();

View File

@ -1,910 +0,0 @@
// dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline
// - Desktop GL: 2.x 3.x 4.x
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// [x] Renderer: Large meshes support (64k+ vertices) with 16-bit indices (Desktop OpenGL only).
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2022-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
// 2022-05-23: OpenGL: Reworking 2021-12-15 "Using buffer orphaning" so it only happens on Intel GPU, seems to cause problems otherwise. (#4468, #4825, #4832, #5127).
// 2022-05-13: OpenGL: Fix state corruption on OpenGL ES 2.0 due to not preserving GL_ELEMENT_ARRAY_BUFFER_BINDING and vertex attribute states.
// 2021-12-15: OpenGL: Using buffer orphaning + glBufferSubData(), seems to fix leaks with multi-viewports with some Intel HD drivers.
// 2021-08-23: OpenGL: Fixed ES 3.0 shader ("#version 300 es") use normal precision floats to avoid wobbly rendering at HD resolutions.
// 2021-08-19: OpenGL: Embed and use our own minimal GL loader (imgui_impl_opengl3_loader.h), removing requirement and support for third-party loader.
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
// 2021-06-25: OpenGL: Use OES_vertex_array extension on Emscripten + backup/restore current state.
// 2021-06-21: OpenGL: Destroy individual vertex/fragment shader objects right after they are linked into the main shader.
// 2021-05-24: OpenGL: Access GL_CLIP_ORIGIN when "GL_ARB_clip_control" extension is detected, inside of just OpenGL 4.5 version.
// 2021-05-19: OpenGL: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
// 2021-04-06: OpenGL: Don't try to read GL_CLIP_ORIGIN unless we're OpenGL 4.5 or greater.
// 2021-02-18: OpenGL: Change blending equation to preserve alpha in output buffer.
// 2021-01-03: OpenGL: Backup, setup and restore GL_STENCIL_TEST state.
// 2020-10-23: OpenGL: Backup, setup and restore GL_PRIMITIVE_RESTART state.
// 2020-10-15: OpenGL: Use glGetString(GL_VERSION) instead of glGetIntegerv(GL_MAJOR_VERSION, ...) when the later returns zero (e.g. Desktop GL 2.x)
// 2020-09-17: OpenGL: Fix to avoid compiling/calling glBindSampler() on ES or pre 3.3 context which have the defines set by a loader.
// 2020-07-10: OpenGL: Added support for glad2 OpenGL loader.
// 2020-05-08: OpenGL: Made default GLSL version 150 (instead of 130) on OSX.
// 2020-04-21: OpenGL: Fixed handling of glClipControl(GL_UPPER_LEFT) by inverting projection matrix.
// 2020-04-12: OpenGL: Fixed context version check mistakenly testing for 4.0+ instead of 3.2+ to enable ImGuiBackendFlags_RendererHasVtxOffset.
// 2020-03-24: OpenGL: Added support for glbinding 2.x OpenGL loader.
// 2020-01-07: OpenGL: Added support for glbinding 3.x OpenGL loader.
// 2019-10-25: OpenGL: Using a combination of GL define and runtime GL version to decide whether to use glDrawElementsBaseVertex(). Fix building with pre-3.2 GL loaders.
// 2019-09-22: OpenGL: Detect default GL loader using __has_include compiler facility.
// 2019-09-16: OpenGL: Tweak initialization code to allow application calling ImGui_ImplOpenGL3_CreateFontsTexture() before the first NewFrame() call.
// 2019-05-29: OpenGL: Desktop GL only: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: OpenGL: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2019-03-29: OpenGL: Not calling glBindBuffer more than necessary in the render loop.
// 2019-03-15: OpenGL: Added a GL call + comments in ImGui_ImplOpenGL3_Init() to detect uninitialized GL function loaders early.
// 2019-03-03: OpenGL: Fix support for ES 2.0 (WebGL 1.0).
// 2019-02-20: OpenGL: Fix for OSX not supporting OpenGL 4.5, we don't try to read GL_CLIP_ORIGIN even if defined by the headers/loader.
// 2019-02-11: OpenGL: Projecting clipping rectangles correctly using draw_data->FramebufferScale to allow multi-viewports for retina display.
// 2019-02-01: OpenGL: Using GLSL 410 shaders for any version over 410 (e.g. 430, 450).
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-11-13: OpenGL: Support for GL 4.5's glClipControl(GL_UPPER_LEFT) / GL_CLIP_ORIGIN.
// 2018-08-29: OpenGL: Added support for more OpenGL loaders: glew and glad, with comments indicative that any loader can be used.
// 2018-08-09: OpenGL: Default to OpenGL ES 3 on iOS and Android. GLSL version default to "#version 300 ES".
// 2018-07-30: OpenGL: Support for GLSL 300 ES and 410 core. Fixes for Emscripten compilation.
// 2018-07-10: OpenGL: Support for more GLSL versions (based on the GLSL version string). Added error output when shaders fail to compile/link.
// 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples.
// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state.
// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer.
// 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150".
// 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself.
// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150.
// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode.
// 2017-05-01: OpenGL: Fixed save and restore of current blend func state.
// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE.
// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle.
// 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752)
//----------------------------------------
// OpenGL GLSL GLSL
// version version string
//----------------------------------------
// 2.0 110 "#version 110"
// 2.1 120 "#version 120"
// 3.0 130 "#version 130"
// 3.1 140 "#version 140"
// 3.2 150 "#version 150"
// 3.3 330 "#version 330 core"
// 4.0 400 "#version 400 core"
// 4.1 410 "#version 410 core"
// 4.2 420 "#version 410 core"
// 4.3 430 "#version 430 core"
// ES 2.0 100 "#version 100" = WebGL 1.0
// ES 3.0 300 "#version 300 es" = WebGL 2.0
//----------------------------------------
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "imgui.h"
#include "imgui_impl_opengl3.h"
#include <stdio.h>
#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
#include <stddef.h> // intptr_t
#else
#include <stdint.h> // intptr_t
#endif
#if defined(__APPLE__)
#include <TargetConditionals.h>
#endif
// Clang warnings with -Weverything
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
#if __has_warning("-Wzero-as-null-pointer-constant")
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
#endif
#endif
// GL includes
#if defined(IMGUI_IMPL_OPENGL_ES2)
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV))
#include <OpenGLES/ES2/gl.h> // Use GL ES 2
#else
#include <GLES2/gl2.h> // Use GL ES 2
#endif
#if defined(__EMSCRIPTEN__)
#ifndef GL_GLEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES
#endif
#include <GLES2/gl2ext.h>
#endif
#elif defined(IMGUI_IMPL_OPENGL_ES3)
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV))
#include <OpenGLES/ES3/gl.h> // Use GL ES 3
#else
#include <GLES3/gl3.h> // Use GL ES 3
#endif
#elif !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
// Modern desktop OpenGL doesn't have a standard portable header file to load OpenGL function pointers.
// Helper libraries are often used for this purpose! Here we are using our own minimal custom loader based on gl3w.
// In the rest of your app/engine, you can use another loader of your choice (gl3w, glew, glad, glbinding, glext, glLoadGen, etc.).
// If you happen to be developing a new feature for this backend (imgui_impl_opengl3.cpp):
// - You may need to regenerate imgui_impl_opengl3_loader.h to add new symbols. See https://github.com/dearimgui/gl3w_stripped
// - You can temporarily use an unstripped version. See https://github.com/dearimgui/gl3w_stripped/releases
// Changes to this backend using new APIs should be accompanied by a regenerated stripped loader version.
#define IMGL3W_IMPL
#include "imgui_impl_opengl3_loader.h"
#endif
// Vertex arrays are not supported on ES2/WebGL1 unless Emscripten which uses an extension
#ifndef IMGUI_IMPL_OPENGL_ES2
#define IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
#elif defined(__EMSCRIPTEN__)
#define IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
#define glBindVertexArray glBindVertexArrayOES
#define glGenVertexArrays glGenVertexArraysOES
#define glDeleteVertexArrays glDeleteVertexArraysOES
#define GL_VERTEX_ARRAY_BINDING GL_VERTEX_ARRAY_BINDING_OES
#endif
// Desktop GL 2.0+ has glPolygonMode() which GL ES and WebGL don't have.
#ifdef GL_POLYGON_MODE
#define IMGUI_IMPL_HAS_POLYGON_MODE
#endif
// Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have.
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_2)
#define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
#endif
// Desktop GL 3.3+ has glBindSampler()
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_3)
#define IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
#endif
// Desktop GL 3.1+ has GL_PRIMITIVE_RESTART state
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && defined(GL_VERSION_3_1)
#define IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
#endif
// Desktop GL use extension detection
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3)
#define IMGUI_IMPL_OPENGL_MAY_HAVE_EXTENSIONS
#endif
// OpenGL Data
struct ImGui_ImplOpenGL3_Data
{
GLuint GlVersion; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2)
char GlslVersionString[32]; // Specified by user or detected based on compile time GL settings.
GLuint FontTexture;
GLuint ShaderHandle;
GLint AttribLocationTex; // Uniforms location
GLint AttribLocationProjMtx;
GLuint AttribLocationVtxPos; // Vertex attributes location
GLuint AttribLocationVtxUV;
GLuint AttribLocationVtxColor;
unsigned int VboHandle, ElementsHandle;
GLsizeiptr VertexBufferSize;
GLsizeiptr IndexBufferSize;
bool HasClipOrigin;
bool UseBufferSubData;
ImGui_ImplOpenGL3_Data() { memset((void*)this, 0, sizeof(*this)); }
};
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
static ImGui_ImplOpenGL3_Data* ImGui_ImplOpenGL3_GetBackendData()
{
return ImGui::GetCurrentContext() ? (ImGui_ImplOpenGL3_Data*)ImGui::GetIO().BackendRendererUserData : NULL;
}
// Forward Declarations
static void ImGui_ImplOpenGL3_InitPlatformInterface();
static void ImGui_ImplOpenGL3_ShutdownPlatformInterface();
// OpenGL vertex attribute state (for ES 1.0 and ES 2.0 only)
#ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
struct ImGui_ImplOpenGL3_VtxAttribState
{
GLint Enabled, Size, Type, Normalized, Stride;
GLvoid* Ptr;
void GetState(GLint index)
{
glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &Enabled);
glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_SIZE, &Size);
glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_TYPE, &Type);
glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &Normalized);
glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &Stride);
glGetVertexAttribPointerv(index, GL_VERTEX_ATTRIB_ARRAY_POINTER, &Ptr);
}
void SetState(GLint index)
{
glVertexAttribPointer(index, Size, Type, (GLboolean)Normalized, Stride, Ptr);
if (Enabled) glEnableVertexAttribArray(index); else glDisableVertexAttribArray(index);
}
};
#endif
// Functions
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
{
ImGuiIO& io = ImGui::GetIO();
IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!");
// Initialize our loader
#if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM)
if (imgl3wInit() != 0)
{
fprintf(stderr, "Failed to initialize OpenGL loader!\n");
return false;
}
#endif
// Setup backend capabilities flags
ImGui_ImplOpenGL3_Data* bd = IM_NEW(ImGui_ImplOpenGL3_Data)();
io.BackendRendererUserData = (void*)bd;
io.BackendRendererName = "imgui_impl_opengl3";
// Query for GL version (e.g. 320 for GL 3.2)
#if !defined(IMGUI_IMPL_OPENGL_ES2)
GLint major = 0;
GLint minor = 0;
glGetIntegerv(GL_MAJOR_VERSION, &major);
glGetIntegerv(GL_MINOR_VERSION, &minor);
if (major == 0 && minor == 0)
{
// Query GL_VERSION in desktop GL 2.x, the string will start with "<major>.<minor>"
const char* gl_version = (const char*)glGetString(GL_VERSION);
sscanf(gl_version, "%d.%d", &major, &minor);
}
bd->GlVersion = (GLuint)(major * 100 + minor * 10);
// Query vendor to enable glBufferSubData kludge
#ifdef _WIN32
if (const char* vendor = (const char*)glGetString(GL_VENDOR))
if (strncmp(vendor, "Intel", 5) == 0)
bd->UseBufferSubData = true;
#endif
//printf("GL_MAJOR_VERSION = %d\nGL_MINOR_VERSION = %d\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", major, minor, (const char*)glGetString(GL_VENDOR), (const char*)glGetString(GL_RENDERER)); // [DEBUG]
#else
bd->GlVersion = 200; // GLES 2
#endif
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
if (bd->GlVersion >= 320)
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
#endif
io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional)
// Store GLSL version string so we can refer to it later in case we recreate shaders.
// Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
if (glsl_version == NULL)
{
#if defined(IMGUI_IMPL_OPENGL_ES2)
glsl_version = "#version 100";
#elif defined(IMGUI_IMPL_OPENGL_ES3)
glsl_version = "#version 300 es";
#elif defined(__APPLE__)
glsl_version = "#version 150";
#else
glsl_version = "#version 130";
#endif
}
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(bd->GlslVersionString));
strcpy(bd->GlslVersionString, glsl_version);
strcat(bd->GlslVersionString, "\n");
// Make an arbitrary GL call (we don't actually need the result)
// IF YOU GET A CRASH HERE: it probably means the OpenGL function loader didn't do its job. Let us know!
GLint current_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
// Detect extensions we support
bd->HasClipOrigin = (bd->GlVersion >= 450);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_EXTENSIONS
GLint num_extensions = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
for (GLint i = 0; i < num_extensions; i++)
{
const char* extension = (const char*)glGetStringi(GL_EXTENSIONS, i);
if (extension != NULL && strcmp(extension, "GL_ARB_clip_control") == 0)
bd->HasClipOrigin = true;
}
#endif
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
ImGui_ImplOpenGL3_InitPlatformInterface();
return true;
}
void ImGui_ImplOpenGL3_Shutdown()
{
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?");
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplOpenGL3_ShutdownPlatformInterface();
ImGui_ImplOpenGL3_DestroyDeviceObjects();
io.BackendRendererName = NULL;
io.BackendRendererUserData = NULL;
IM_DELETE(bd);
}
void ImGui_ImplOpenGL3_NewFrame()
{
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
IM_ASSERT(bd != NULL && "Did you call ImGui_ImplOpenGL3_Init()?");
if (!bd->ShaderHandle)
ImGui_ImplOpenGL3_CreateDeviceObjects();
}
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
{
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glEnable(GL_SCISSOR_TEST);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
if (bd->GlVersion >= 310)
glDisable(GL_PRIMITIVE_RESTART);
#endif
#ifdef IMGUI_IMPL_HAS_POLYGON_MODE
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
#endif
// Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
#if defined(GL_CLIP_ORIGIN)
bool clip_origin_lower_left = true;
if (bd->HasClipOrigin)
{
GLenum current_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&current_clip_origin);
if (current_clip_origin == GL_UPPER_LEFT)
clip_origin_lower_left = false;
}
#endif
// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
#if defined(GL_CLIP_ORIGIN)
if (!clip_origin_lower_left) { float tmp = T; T = B; B = tmp; } // Swap top and bottom if origin is upper left
#endif
const float ortho_projection[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, -1.0f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f },
};
glUseProgram(bd->ShaderHandle);
glUniform1i(bd->AttribLocationTex, 0);
glUniformMatrix4fv(bd->AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
if (bd->GlVersion >= 330)
glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise.
#endif
(void)vertex_array_object;
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
glBindVertexArray(vertex_array_object);
#endif
// Bind vertex/index buffers and setup attributes for ImDrawVert
glBindBuffer(GL_ARRAY_BUFFER, bd->VboHandle);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bd->ElementsHandle);
glEnableVertexAttribArray(bd->AttribLocationVtxPos);
glEnableVertexAttribArray(bd->AttribLocationVtxUV);
glEnableVertexAttribArray(bd->AttribLocationVtxColor);
glVertexAttribPointer(bd->AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos));
glVertexAttribPointer(bd->AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv));
glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col));
}
// OpenGL3 Render function.
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly.
// This is in order to be able to run within an OpenGL engine that doesn't do so.
void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
{
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
if (fb_width <= 0 || fb_height <= 0)
return;
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
// Backup GL state
GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
glActiveTexture(GL_TEXTURE0);
GLuint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&last_program);
GLuint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&last_texture);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
GLuint last_sampler; if (bd->GlVersion >= 330) { glGetIntegerv(GL_SAMPLER_BINDING, (GLint*)&last_sampler); } else { last_sampler = 0; }
#endif
GLuint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint*)&last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
// This is part of VAO on OpenGL 3.0+ and OpenGL ES 3.0+.
GLint last_element_array_buffer; glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &last_element_array_buffer);
ImGui_ImplOpenGL3_VtxAttribState last_vtx_attrib_state_pos; last_vtx_attrib_state_pos.GetState(bd->AttribLocationVtxPos);
ImGui_ImplOpenGL3_VtxAttribState last_vtx_attrib_state_uv; last_vtx_attrib_state_uv.GetState(bd->AttribLocationVtxUV);
ImGui_ImplOpenGL3_VtxAttribState last_vtx_attrib_state_color; last_vtx_attrib_state_color.GetState(bd->AttribLocationVtxColor);
#endif
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, (GLint*)&last_vertex_array_object);
#endif
#ifdef IMGUI_IMPL_HAS_POLYGON_MODE
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
#endif
GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);
GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb);
GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb);
GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha);
GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha);
GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb);
GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha);
GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
GLboolean last_enable_stencil_test = glIsEnabled(GL_STENCIL_TEST);
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
GLboolean last_enable_primitive_restart = (bd->GlVersion >= 310) ? glIsEnabled(GL_PRIMITIVE_RESTART) : GL_FALSE;
#endif
// Setup desired GL state
// Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
// The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.
GLuint vertex_array_object = 0;
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
glGenVertexArrays(1, &vertex_array_object);
#endif
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
// Will project scissor/clipping rectangles into framebuffer space
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
// Render command lists
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
// Upload vertex/index buffers
// - On Intel windows drivers we got reports that regular glBufferData() led to accumulating leaks when using multi-viewports, so we started using orphaning + glBufferSubData(). (See https://github.com/ocornut/imgui/issues/4468)
// - On NVIDIA drivers we got reports that using orphaning + glBufferSubData() led to glitches when using multi-viewports.
// - OpenGL drivers are in a very sorry state in 2022, for now we are switching code path based on vendors.
const GLsizeiptr vtx_buffer_size = (GLsizeiptr)cmd_list->VtxBuffer.Size * (int)sizeof(ImDrawVert);
const GLsizeiptr idx_buffer_size = (GLsizeiptr)cmd_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx);
if (bd->UseBufferSubData)
{
if (bd->VertexBufferSize < vtx_buffer_size)
{
bd->VertexBufferSize = vtx_buffer_size;
glBufferData(GL_ARRAY_BUFFER, bd->VertexBufferSize, NULL, GL_STREAM_DRAW);
}
if (bd->IndexBufferSize < idx_buffer_size)
{
bd->IndexBufferSize = idx_buffer_size;
glBufferData(GL_ELEMENT_ARRAY_BUFFER, bd->IndexBufferSize, NULL, GL_STREAM_DRAW);
}
glBufferSubData(GL_ARRAY_BUFFER, 0, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data);
}
else
{
glBufferData(GL_ARRAY_BUFFER, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW);
}
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
continue;
// Apply scissor/clipping rectangle (Y is inverted in OpenGL)
glScissor((int)clip_min.x, (int)((float)fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y));
// Bind texture, Draw
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID());
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
if (bd->GlVersion >= 320)
glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset);
else
#endif
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)));
}
}
}
// Destroy the temporary VAO
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
glDeleteVertexArrays(1, &vertex_array_object);
#endif
// Restore modified GL state
glUseProgram(last_program);
glBindTexture(GL_TEXTURE_2D, last_texture);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_BIND_SAMPLER
if (bd->GlVersion >= 330)
glBindSampler(0, last_sampler);
#endif
glActiveTexture(last_active_texture);
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
glBindVertexArray(last_vertex_array_object);
#endif
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, last_element_array_buffer);
last_vtx_attrib_state_pos.SetState(bd->AttribLocationVtxPos);
last_vtx_attrib_state_uv.SetState(bd->AttribLocationVtxUV);
last_vtx_attrib_state_color.SetState(bd->AttribLocationVtxColor);
#endif
glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);
if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
if (last_enable_stencil_test) glEnable(GL_STENCIL_TEST); else glDisable(GL_STENCIL_TEST);
if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
#ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_PRIMITIVE_RESTART
if (bd->GlVersion >= 310) { if (last_enable_primitive_restart) glEnable(GL_PRIMITIVE_RESTART); else glDisable(GL_PRIMITIVE_RESTART); }
#endif
#ifdef IMGUI_IMPL_HAS_POLYGON_MODE
glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
#endif
glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
(void)bd; // Not all compilation paths use this
}
bool ImGui_ImplOpenGL3_CreateFontsTexture()
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
// Build texture atlas
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
// Upload texture to graphics system
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
GLint last_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGenTextures(1, &bd->FontTexture);
glBindTexture(GL_TEXTURE_2D, bd->FontTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
#ifdef GL_UNPACK_ROW_LENGTH // Not on WebGL/ES
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
#endif
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Store our identifier
io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture);
// Restore state
glBindTexture(GL_TEXTURE_2D, last_texture);
return true;
}
void ImGui_ImplOpenGL3_DestroyFontsTexture()
{
ImGuiIO& io = ImGui::GetIO();
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
if (bd->FontTexture)
{
glDeleteTextures(1, &bd->FontTexture);
io.Fonts->SetTexID(0);
bd->FontTexture = 0;
}
}
// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.
static bool CheckShader(GLuint handle, const char* desc)
{
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
GLint status = 0, log_length = 0;
glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean)status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s! With GLSL: %s\n", desc, bd->GlslVersionString);
if (log_length > 1)
{
ImVector<char> buf;
buf.resize((int)(log_length + 1));
glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}
return (GLboolean)status == GL_TRUE;
}
// If you get an error please report on GitHub. You may try different GL context version or GLSL version.
static bool CheckProgram(GLuint handle, const char* desc)
{
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
GLint status = 0, log_length = 0;
glGetProgramiv(handle, GL_LINK_STATUS, &status);
glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean)status == GL_FALSE)
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! With GLSL %s\n", desc, bd->GlslVersionString);
if (log_length > 1)
{
ImVector<char> buf;
buf.resize((int)(log_length + 1));
glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}
return (GLboolean)status == GL_TRUE;
}
bool ImGui_ImplOpenGL3_CreateDeviceObjects()
{
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
// Backup GL state
GLint last_texture, last_array_buffer;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
GLint last_vertex_array;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
#endif
// Parse GLSL version string
int glsl_version = 130;
sscanf(bd->GlslVersionString, "#version %d", &glsl_version);
const GLchar* vertex_shader_glsl_120 =
"uniform mat4 ProjMtx;\n"
"attribute vec2 Position;\n"
"attribute vec2 UV;\n"
"attribute vec4 Color;\n"
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_130 =
"uniform mat4 ProjMtx;\n"
"in vec2 Position;\n"
"in vec2 UV;\n"
"in vec4 Color;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_300_es =
"precision highp float;\n"
"layout (location = 0) in vec2 Position;\n"
"layout (location = 1) in vec2 UV;\n"
"layout (location = 2) in vec4 Color;\n"
"uniform mat4 ProjMtx;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* vertex_shader_glsl_410_core =
"layout (location = 0) in vec2 Position;\n"
"layout (location = 1) in vec2 UV;\n"
"layout (location = 2) in vec4 Color;\n"
"uniform mat4 ProjMtx;\n"
"out vec2 Frag_UV;\n"
"out vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" Frag_UV = UV;\n"
" Frag_Color = Color;\n"
" gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
"}\n";
const GLchar* fragment_shader_glsl_120 =
"#ifdef GL_ES\n"
" precision mediump float;\n"
"#endif\n"
"uniform sampler2D Texture;\n"
"varying vec2 Frag_UV;\n"
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_130 =
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_300_es =
"precision mediump float;\n"
"uniform sampler2D Texture;\n"
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_410_core =
"in vec2 Frag_UV;\n"
"in vec4 Frag_Color;\n"
"uniform sampler2D Texture;\n"
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
// Select shaders matching our GLSL versions
const GLchar* vertex_shader = NULL;
const GLchar* fragment_shader = NULL;
if (glsl_version < 130)
{
vertex_shader = vertex_shader_glsl_120;
fragment_shader = fragment_shader_glsl_120;
}
else if (glsl_version >= 410)
{
vertex_shader = vertex_shader_glsl_410_core;
fragment_shader = fragment_shader_glsl_410_core;
}
else if (glsl_version == 300)
{
vertex_shader = vertex_shader_glsl_300_es;
fragment_shader = fragment_shader_glsl_300_es;
}
else
{
vertex_shader = vertex_shader_glsl_130;
fragment_shader = fragment_shader_glsl_130;
}
// Create shaders
const GLchar* vertex_shader_with_version[2] = { bd->GlslVersionString, vertex_shader };
GLuint vert_handle = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vert_handle, 2, vertex_shader_with_version, NULL);
glCompileShader(vert_handle);
CheckShader(vert_handle, "vertex shader");
const GLchar* fragment_shader_with_version[2] = { bd->GlslVersionString, fragment_shader };
GLuint frag_handle = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(frag_handle, 2, fragment_shader_with_version, NULL);
glCompileShader(frag_handle);
CheckShader(frag_handle, "fragment shader");
// Link
bd->ShaderHandle = glCreateProgram();
glAttachShader(bd->ShaderHandle, vert_handle);
glAttachShader(bd->ShaderHandle, frag_handle);
glLinkProgram(bd->ShaderHandle);
CheckProgram(bd->ShaderHandle, "shader program");
glDetachShader(bd->ShaderHandle, vert_handle);
glDetachShader(bd->ShaderHandle, frag_handle);
glDeleteShader(vert_handle);
glDeleteShader(frag_handle);
bd->AttribLocationTex = glGetUniformLocation(bd->ShaderHandle, "Texture");
bd->AttribLocationProjMtx = glGetUniformLocation(bd->ShaderHandle, "ProjMtx");
bd->AttribLocationVtxPos = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Position");
bd->AttribLocationVtxUV = (GLuint)glGetAttribLocation(bd->ShaderHandle, "UV");
bd->AttribLocationVtxColor = (GLuint)glGetAttribLocation(bd->ShaderHandle, "Color");
// Create buffers
glGenBuffers(1, &bd->VboHandle);
glGenBuffers(1, &bd->ElementsHandle);
ImGui_ImplOpenGL3_CreateFontsTexture();
// Restore modified GL state
glBindTexture(GL_TEXTURE_2D, last_texture);
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
#ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY
glBindVertexArray(last_vertex_array);
#endif
return true;
}
void ImGui_ImplOpenGL3_DestroyDeviceObjects()
{
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
if (bd->VboHandle) { glDeleteBuffers(1, &bd->VboHandle); bd->VboHandle = 0; }
if (bd->ElementsHandle) { glDeleteBuffers(1, &bd->ElementsHandle); bd->ElementsHandle = 0; }
if (bd->ShaderHandle) { glDeleteProgram(bd->ShaderHandle); bd->ShaderHandle = 0; }
ImGui_ImplOpenGL3_DestroyFontsTexture();
}
//--------------------------------------------------------------------------------------------------------
// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
//--------------------------------------------------------------------------------------------------------
static void ImGui_ImplOpenGL3_RenderWindow(ImGuiViewport* viewport, void*)
{
if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear))
{
ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w);
glClear(GL_COLOR_BUFFER_BIT);
}
ImGui_ImplOpenGL3_RenderDrawData(viewport->DrawData);
}
static void ImGui_ImplOpenGL3_InitPlatformInterface()
{
ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
platform_io.Renderer_RenderWindow = ImGui_ImplOpenGL3_RenderWindow;
}
static void ImGui_ImplOpenGL3_ShutdownPlatformInterface()
{
ImGui::DestroyPlatformWindows();
}
#if defined(__clang__)
#pragma clang diagnostic pop
#endif

View File

@ -1,56 +0,0 @@
// dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline
// - Desktop GL: 2.x 3.x 4.x
// - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0)
// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..)
// Implemented features:
// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
// [x] Renderer: Large meshes support (64k+ vertices) with 16-bit indices (Desktop OpenGL only).
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// About GLSL version:
// The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string.
// On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es"
// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp.
#pragma once
#include "imgui.h" // IMGUI_IMPL_API
// Backend API
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL);
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
// (Optional) Called by Init/NewFrame/Shutdown
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
// Specific OpenGL ES versions
//#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten
//#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android
// You can explicitly select GLES2 or GLES3 API by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line.
#if !defined(IMGUI_IMPL_OPENGL_ES2) \
&& !defined(IMGUI_IMPL_OPENGL_ES3)
// Try to detect GLES on matching platforms
#if defined(__APPLE__)
#include <TargetConditionals.h>
#endif
#if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__))
#define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es"
#elif defined(__EMSCRIPTEN__) || defined(__amigaos4__)
#define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100"
#else
// Otherwise imgui_impl_opengl3_loader.h will be used.
#endif
#endif

View File

@ -1,786 +0,0 @@
//-----------------------------------------------------------------------------
// About imgui_impl_opengl3_loader.h:
//
// We embed our own OpenGL loader to not require user to provide their own or to have to use ours,
// which proved to be endless problems for users.
// Our loader is custom-generated, based on gl3w but automatically filtered to only include
// enums/functions that we use in our imgui_impl_opengl3.cpp source file in order to be small.
//
// YOU SHOULD NOT NEED TO INCLUDE/USE THIS DIRECTLY. THIS IS USED BY imgui_impl_opengl3.cpp ONLY.
// THE REST OF YOUR APP SHOULD USE A DIFFERENT GL LOADER: ANY GL LOADER OF YOUR CHOICE.
//
// Regenerate with:
// python gl3w_gen.py --output ../imgui/backends/imgui_impl_opengl3_loader.h --ref ../imgui/backends/imgui_impl_opengl3.cpp ./extra_symbols.txt
//
// More info:
// https://github.com/dearimgui/gl3w_stripped
// https://github.com/ocornut/imgui/issues/4445
//-----------------------------------------------------------------------------
/*
* This file was generated with gl3w_gen.py, part of imgl3w
* (hosted at https://github.com/dearimgui/gl3w_stripped)
*
* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* THE SOFTWARE IS 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 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 SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __gl3w_h_
#define __gl3w_h_
// Adapted from KHR/khrplatform.h to avoid including entire file.
#ifndef __khrplatform_h_
typedef float khronos_float_t;
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;
#ifdef _WIN64
typedef signed long long int khronos_intptr_t;
typedef signed long long int khronos_ssize_t;
#else
typedef signed long int khronos_intptr_t;
typedef signed long int khronos_ssize_t;
#endif
#if defined(_MSC_VER) && !defined(__clang__)
typedef signed __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#elif (defined(__clang__) || defined(__GNUC__)) && (__cplusplus < 201100)
#include <stdint.h>
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#else
typedef signed long long khronos_int64_t;
typedef unsigned long long khronos_uint64_t;
#endif
#endif // __khrplatform_h_
#ifndef __gl_glcorearb_h_
#define __gl_glcorearb_h_ 1
#ifdef __cplusplus
extern "C" {
#endif
/*
** Copyright 2013-2020 The Khronos Group Inc.
** SPDX-License-Identifier: MIT
**
** This header is generated from the Khronos OpenGL / OpenGL ES XML
** API Registry. The current version of the Registry, generator scripts
** used to make the header, and the header can be found at
** https://github.com/KhronosGroup/OpenGL-Registry
*/
#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#include <windows.h>
#endif
#ifndef APIENTRY
#define APIENTRY
#endif
#ifndef APIENTRYP
#define APIENTRYP APIENTRY *
#endif
#ifndef GLAPI
#define GLAPI extern
#endif
/* glcorearb.h is for use with OpenGL core profile implementations.
** It should should be placed in the same directory as gl.h and
** included as <GL/glcorearb.h>.
**
** glcorearb.h includes only APIs in the latest OpenGL core profile
** implementation together with APIs in newer ARB extensions which
** can be supported by the core profile. It does not, and never will
** include functionality removed from the core profile, such as
** fixed-function vertex and fragment processing.
**
** Do not #include both <GL/glcorearb.h> and either of <GL/gl.h> or
** <GL/glext.h> in the same source file.
*/
/* Generated C header for:
* API: gl
* Profile: core
* Versions considered: .*
* Versions emitted: .*
* Default extensions included: glcore
* Additional extensions included: _nomatch_^
* Extensions removed: _nomatch_^
*/
#ifndef GL_VERSION_1_0
typedef void GLvoid;
typedef unsigned int GLenum;
typedef khronos_float_t GLfloat;
typedef int GLint;
typedef int GLsizei;
typedef unsigned int GLbitfield;
typedef double GLdouble;
typedef unsigned int GLuint;
typedef unsigned char GLboolean;
typedef khronos_uint8_t GLubyte;
#define GL_COLOR_BUFFER_BIT 0x00004000
#define GL_FALSE 0
#define GL_TRUE 1
#define GL_TRIANGLES 0x0004
#define GL_ONE 1
#define GL_SRC_ALPHA 0x0302
#define GL_ONE_MINUS_SRC_ALPHA 0x0303
#define GL_FRONT_AND_BACK 0x0408
#define GL_POLYGON_MODE 0x0B40
#define GL_CULL_FACE 0x0B44
#define GL_DEPTH_TEST 0x0B71
#define GL_STENCIL_TEST 0x0B90
#define GL_VIEWPORT 0x0BA2
#define GL_BLEND 0x0BE2
#define GL_SCISSOR_BOX 0x0C10
#define GL_SCISSOR_TEST 0x0C11
#define GL_UNPACK_ROW_LENGTH 0x0CF2
#define GL_PACK_ALIGNMENT 0x0D05
#define GL_TEXTURE_2D 0x0DE1
#define GL_UNSIGNED_BYTE 0x1401
#define GL_UNSIGNED_SHORT 0x1403
#define GL_UNSIGNED_INT 0x1405
#define GL_FLOAT 0x1406
#define GL_RGBA 0x1908
#define GL_FILL 0x1B02
#define GL_VENDOR 0x1F00
#define GL_RENDERER 0x1F01
#define GL_VERSION 0x1F02
#define GL_EXTENSIONS 0x1F03
#define GL_LINEAR 0x2601
#define GL_TEXTURE_MAG_FILTER 0x2800
#define GL_TEXTURE_MIN_FILTER 0x2801
typedef void (APIENTRYP PFNGLPOLYGONMODEPROC) (GLenum face, GLenum mode);
typedef void (APIENTRYP PFNGLSCISSORPROC) (GLint x, GLint y, GLsizei width, GLsizei height);
typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC) (GLenum target, GLenum pname, GLint param);
typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);
typedef void (APIENTRYP PFNGLCLEARPROC) (GLbitfield mask);
typedef void (APIENTRYP PFNGLCLEARCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
typedef void (APIENTRYP PFNGLDISABLEPROC) (GLenum cap);
typedef void (APIENTRYP PFNGLENABLEPROC) (GLenum cap);
typedef void (APIENTRYP PFNGLFLUSHPROC) (void);
typedef void (APIENTRYP PFNGLPIXELSTOREIPROC) (GLenum pname, GLint param);
typedef void (APIENTRYP PFNGLREADPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels);
typedef GLenum (APIENTRYP PFNGLGETERRORPROC) (void);
typedef void (APIENTRYP PFNGLGETINTEGERVPROC) (GLenum pname, GLint *data);
typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGPROC) (GLenum name);
typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC) (GLenum cap);
typedef void (APIENTRYP PFNGLVIEWPORTPROC) (GLint x, GLint y, GLsizei width, GLsizei height);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glPolygonMode (GLenum face, GLenum mode);
GLAPI void APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height);
GLAPI void APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param);
GLAPI void APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels);
GLAPI void APIENTRY glClear (GLbitfield mask);
GLAPI void APIENTRY glClearColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
GLAPI void APIENTRY glDisable (GLenum cap);
GLAPI void APIENTRY glEnable (GLenum cap);
GLAPI void APIENTRY glFlush (void);
GLAPI void APIENTRY glPixelStorei (GLenum pname, GLint param);
GLAPI void APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels);
GLAPI GLenum APIENTRY glGetError (void);
GLAPI void APIENTRY glGetIntegerv (GLenum pname, GLint *data);
GLAPI const GLubyte *APIENTRY glGetString (GLenum name);
GLAPI GLboolean APIENTRY glIsEnabled (GLenum cap);
GLAPI void APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height);
#endif
#endif /* GL_VERSION_1_0 */
#ifndef GL_VERSION_1_1
typedef khronos_float_t GLclampf;
typedef double GLclampd;
#define GL_TEXTURE_BINDING_2D 0x8069
typedef void (APIENTRYP PFNGLDRAWELEMENTSPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices);
typedef void (APIENTRYP PFNGLBINDTEXTUREPROC) (GLenum target, GLuint texture);
typedef void (APIENTRYP PFNGLDELETETEXTURESPROC) (GLsizei n, const GLuint *textures);
typedef void (APIENTRYP PFNGLGENTEXTURESPROC) (GLsizei n, GLuint *textures);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const void *indices);
GLAPI void APIENTRY glBindTexture (GLenum target, GLuint texture);
GLAPI void APIENTRY glDeleteTextures (GLsizei n, const GLuint *textures);
GLAPI void APIENTRY glGenTextures (GLsizei n, GLuint *textures);
#endif
#endif /* GL_VERSION_1_1 */
#ifndef GL_VERSION_1_3
#define GL_TEXTURE0 0x84C0
#define GL_ACTIVE_TEXTURE 0x84E0
typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glActiveTexture (GLenum texture);
#endif
#endif /* GL_VERSION_1_3 */
#ifndef GL_VERSION_1_4
#define GL_BLEND_DST_RGB 0x80C8
#define GL_BLEND_SRC_RGB 0x80C9
#define GL_BLEND_DST_ALPHA 0x80CA
#define GL_BLEND_SRC_ALPHA 0x80CB
#define GL_FUNC_ADD 0x8006
typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
GLAPI void APIENTRY glBlendEquation (GLenum mode);
#endif
#endif /* GL_VERSION_1_4 */
#ifndef GL_VERSION_1_5
typedef khronos_ssize_t GLsizeiptr;
typedef khronos_intptr_t GLintptr;
#define GL_ARRAY_BUFFER 0x8892
#define GL_ELEMENT_ARRAY_BUFFER 0x8893
#define GL_ARRAY_BUFFER_BINDING 0x8894
#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895
#define GL_STREAM_DRAW 0x88E0
typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer);
typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers);
typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers);
typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage);
typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer);
GLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers);
GLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers);
GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage);
GLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data);
#endif
#endif /* GL_VERSION_1_5 */
#ifndef GL_VERSION_2_0
typedef char GLchar;
typedef khronos_int16_t GLshort;
typedef khronos_int8_t GLbyte;
typedef khronos_uint16_t GLushort;
#define GL_BLEND_EQUATION_RGB 0x8009
#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622
#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623
#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624
#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625
#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645
#define GL_BLEND_EQUATION_ALPHA 0x883D
#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A
#define GL_FRAGMENT_SHADER 0x8B30
#define GL_VERTEX_SHADER 0x8B31
#define GL_COMPILE_STATUS 0x8B81
#define GL_LINK_STATUS 0x8B82
#define GL_INFO_LOG_LENGTH 0x8B84
#define GL_CURRENT_PROGRAM 0x8B8D
#define GL_UPPER_LEFT 0x8CA2
typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha);
typedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader);
typedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader);
typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void);
typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type);
typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program);
typedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader);
typedef void (APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader);
typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index);
typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index);
typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name);
typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params);
typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
typedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params);
typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name);
typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params);
typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, void **pointer);
typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program);
typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);
typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program);
typedef void (APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0);
typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha);
GLAPI void APIENTRY glAttachShader (GLuint program, GLuint shader);
GLAPI void APIENTRY glCompileShader (GLuint shader);
GLAPI GLuint APIENTRY glCreateProgram (void);
GLAPI GLuint APIENTRY glCreateShader (GLenum type);
GLAPI void APIENTRY glDeleteProgram (GLuint program);
GLAPI void APIENTRY glDeleteShader (GLuint shader);
GLAPI void APIENTRY glDetachShader (GLuint program, GLuint shader);
GLAPI void APIENTRY glDisableVertexAttribArray (GLuint index);
GLAPI void APIENTRY glEnableVertexAttribArray (GLuint index);
GLAPI GLint APIENTRY glGetAttribLocation (GLuint program, const GLchar *name);
GLAPI void APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint *params);
GLAPI void APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
GLAPI void APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint *params);
GLAPI void APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
GLAPI GLint APIENTRY glGetUniformLocation (GLuint program, const GLchar *name);
GLAPI void APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params);
GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void **pointer);
GLAPI void APIENTRY glLinkProgram (GLuint program);
GLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);
GLAPI void APIENTRY glUseProgram (GLuint program);
GLAPI void APIENTRY glUniform1i (GLint location, GLint v0);
GLAPI void APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value);
GLAPI void APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
#endif
#endif /* GL_VERSION_2_0 */
#ifndef GL_VERSION_3_0
typedef khronos_uint16_t GLhalf;
#define GL_MAJOR_VERSION 0x821B
#define GL_MINOR_VERSION 0x821C
#define GL_NUM_EXTENSIONS 0x821D
#define GL_FRAMEBUFFER_SRGB 0x8DB9
#define GL_VERTEX_ARRAY_BINDING 0x85B5
typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC) (GLenum target, GLuint index, GLboolean *data);
typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC) (GLenum target, GLuint index, GLint *data);
typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint index);
typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC) (GLuint array);
typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays);
typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI const GLubyte *APIENTRY glGetStringi (GLenum name, GLuint index);
GLAPI void APIENTRY glBindVertexArray (GLuint array);
GLAPI void APIENTRY glDeleteVertexArrays (GLsizei n, const GLuint *arrays);
GLAPI void APIENTRY glGenVertexArrays (GLsizei n, GLuint *arrays);
#endif
#endif /* GL_VERSION_3_0 */
#ifndef GL_VERSION_3_1
#define GL_VERSION_3_1 1
#define GL_PRIMITIVE_RESTART 0x8F9D
#endif /* GL_VERSION_3_1 */
#ifndef GL_VERSION_3_2
#define GL_VERSION_3_2 1
typedef struct __GLsync *GLsync;
typedef khronos_uint64_t GLuint64;
typedef khronos_int64_t GLint64;
typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex);
typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64 *data);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex);
#endif
#endif /* GL_VERSION_3_2 */
#ifndef GL_VERSION_3_3
#define GL_VERSION_3_3 1
#define GL_SAMPLER_BINDING 0x8919
typedef void (APIENTRYP PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler);
#ifdef GL_GLEXT_PROTOTYPES
GLAPI void APIENTRY glBindSampler (GLuint unit, GLuint sampler);
#endif
#endif /* GL_VERSION_3_3 */
#ifndef GL_VERSION_4_1
typedef void (APIENTRYP PFNGLGETFLOATI_VPROC) (GLenum target, GLuint index, GLfloat *data);
typedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC) (GLenum target, GLuint index, GLdouble *data);
#endif /* GL_VERSION_4_1 */
#ifndef GL_VERSION_4_3
typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
#endif /* GL_VERSION_4_3 */
#ifndef GL_VERSION_4_5
#define GL_CLIP_ORIGIN 0x935C
typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint *param);
typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI64_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint64 *param);
#endif /* GL_VERSION_4_5 */
#ifndef GL_ARB_bindless_texture
typedef khronos_uint64_t GLuint64EXT;
#endif /* GL_ARB_bindless_texture */
#ifndef GL_ARB_cl_event
struct _cl_context;
struct _cl_event;
#endif /* GL_ARB_cl_event */
#ifndef GL_ARB_clip_control
#define GL_ARB_clip_control 1
#endif /* GL_ARB_clip_control */
#ifndef GL_ARB_debug_output
typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);
#endif /* GL_ARB_debug_output */
#ifndef GL_EXT_EGL_image_storage
typedef void *GLeglImageOES;
#endif /* GL_EXT_EGL_image_storage */
#ifndef GL_EXT_direct_state_access
typedef void (APIENTRYP PFNGLGETFLOATI_VEXTPROC) (GLenum pname, GLuint index, GLfloat *params);
typedef void (APIENTRYP PFNGLGETDOUBLEI_VEXTPROC) (GLenum pname, GLuint index, GLdouble *params);
typedef void (APIENTRYP PFNGLGETPOINTERI_VEXTPROC) (GLenum pname, GLuint index, void **params);
typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param);
typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, void **param);
#endif /* GL_EXT_direct_state_access */
#ifndef GL_NV_draw_vulkan_image
typedef void (APIENTRY *GLVULKANPROCNV)(void);
#endif /* GL_NV_draw_vulkan_image */
#ifndef GL_NV_gpu_shader5
typedef khronos_int64_t GLint64EXT;
#endif /* GL_NV_gpu_shader5 */
#ifndef GL_NV_vertex_buffer_unified_memory
typedef void (APIENTRYP PFNGLGETINTEGERUI64I_VNVPROC) (GLenum value, GLuint index, GLuint64EXT *result);
#endif /* GL_NV_vertex_buffer_unified_memory */
#ifdef __cplusplus
}
#endif
#endif
#ifndef GL3W_API
#define GL3W_API
#endif
#ifndef __gl_h_
#define __gl_h_
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define GL3W_OK 0
#define GL3W_ERROR_INIT -1
#define GL3W_ERROR_LIBRARY_OPEN -2
#define GL3W_ERROR_OPENGL_VERSION -3
typedef void (*GL3WglProc)(void);
typedef GL3WglProc (*GL3WGetProcAddressProc)(const char *proc);
/* gl3w api */
GL3W_API int imgl3wInit(void);
GL3W_API int imgl3wInit2(GL3WGetProcAddressProc proc);
GL3W_API int imgl3wIsSupported(int major, int minor);
GL3W_API GL3WglProc imgl3wGetProcAddress(const char *proc);
/* gl3w internal state */
union GL3WProcs {
GL3WglProc ptr[58];
struct {
PFNGLACTIVETEXTUREPROC ActiveTexture;
PFNGLATTACHSHADERPROC AttachShader;
PFNGLBINDBUFFERPROC BindBuffer;
PFNGLBINDSAMPLERPROC BindSampler;
PFNGLBINDTEXTUREPROC BindTexture;
PFNGLBINDVERTEXARRAYPROC BindVertexArray;
PFNGLBLENDEQUATIONPROC BlendEquation;
PFNGLBLENDEQUATIONSEPARATEPROC BlendEquationSeparate;
PFNGLBLENDFUNCSEPARATEPROC BlendFuncSeparate;
PFNGLBUFFERDATAPROC BufferData;
PFNGLBUFFERSUBDATAPROC BufferSubData;
PFNGLCLEARPROC Clear;
PFNGLCLEARCOLORPROC ClearColor;
PFNGLCOMPILESHADERPROC CompileShader;
PFNGLCREATEPROGRAMPROC CreateProgram;
PFNGLCREATESHADERPROC CreateShader;
PFNGLDELETEBUFFERSPROC DeleteBuffers;
PFNGLDELETEPROGRAMPROC DeleteProgram;
PFNGLDELETESHADERPROC DeleteShader;
PFNGLDELETETEXTURESPROC DeleteTextures;
PFNGLDELETEVERTEXARRAYSPROC DeleteVertexArrays;
PFNGLDETACHSHADERPROC DetachShader;
PFNGLDISABLEPROC Disable;
PFNGLDISABLEVERTEXATTRIBARRAYPROC DisableVertexAttribArray;
PFNGLDRAWELEMENTSPROC DrawElements;
PFNGLDRAWELEMENTSBASEVERTEXPROC DrawElementsBaseVertex;
PFNGLENABLEPROC Enable;
PFNGLENABLEVERTEXATTRIBARRAYPROC EnableVertexAttribArray;
PFNGLFLUSHPROC Flush;
PFNGLGENBUFFERSPROC GenBuffers;
PFNGLGENTEXTURESPROC GenTextures;
PFNGLGENVERTEXARRAYSPROC GenVertexArrays;
PFNGLGETATTRIBLOCATIONPROC GetAttribLocation;
PFNGLGETERRORPROC GetError;
PFNGLGETINTEGERVPROC GetIntegerv;
PFNGLGETPROGRAMINFOLOGPROC GetProgramInfoLog;
PFNGLGETPROGRAMIVPROC GetProgramiv;
PFNGLGETSHADERINFOLOGPROC GetShaderInfoLog;
PFNGLGETSHADERIVPROC GetShaderiv;
PFNGLGETSTRINGPROC GetString;
PFNGLGETSTRINGIPROC GetStringi;
PFNGLGETUNIFORMLOCATIONPROC GetUniformLocation;
PFNGLGETVERTEXATTRIBPOINTERVPROC GetVertexAttribPointerv;
PFNGLGETVERTEXATTRIBIVPROC GetVertexAttribiv;
PFNGLISENABLEDPROC IsEnabled;
PFNGLLINKPROGRAMPROC LinkProgram;
PFNGLPIXELSTOREIPROC PixelStorei;
PFNGLPOLYGONMODEPROC PolygonMode;
PFNGLREADPIXELSPROC ReadPixels;
PFNGLSCISSORPROC Scissor;
PFNGLSHADERSOURCEPROC ShaderSource;
PFNGLTEXIMAGE2DPROC TexImage2D;
PFNGLTEXPARAMETERIPROC TexParameteri;
PFNGLUNIFORM1IPROC Uniform1i;
PFNGLUNIFORMMATRIX4FVPROC UniformMatrix4fv;
PFNGLUSEPROGRAMPROC UseProgram;
PFNGLVERTEXATTRIBPOINTERPROC VertexAttribPointer;
PFNGLVIEWPORTPROC Viewport;
} gl;
};
GL3W_API extern union GL3WProcs imgl3wProcs;
/* OpenGL functions */
#define glActiveTexture imgl3wProcs.gl.ActiveTexture
#define glAttachShader imgl3wProcs.gl.AttachShader
#define glBindBuffer imgl3wProcs.gl.BindBuffer
#define glBindSampler imgl3wProcs.gl.BindSampler
#define glBindTexture imgl3wProcs.gl.BindTexture
#define glBindVertexArray imgl3wProcs.gl.BindVertexArray
#define glBlendEquation imgl3wProcs.gl.BlendEquation
#define glBlendEquationSeparate imgl3wProcs.gl.BlendEquationSeparate
#define glBlendFuncSeparate imgl3wProcs.gl.BlendFuncSeparate
#define glBufferData imgl3wProcs.gl.BufferData
#define glBufferSubData imgl3wProcs.gl.BufferSubData
#define glClear imgl3wProcs.gl.Clear
#define glClearColor imgl3wProcs.gl.ClearColor
#define glCompileShader imgl3wProcs.gl.CompileShader
#define glCreateProgram imgl3wProcs.gl.CreateProgram
#define glCreateShader imgl3wProcs.gl.CreateShader
#define glDeleteBuffers imgl3wProcs.gl.DeleteBuffers
#define glDeleteProgram imgl3wProcs.gl.DeleteProgram
#define glDeleteShader imgl3wProcs.gl.DeleteShader
#define glDeleteTextures imgl3wProcs.gl.DeleteTextures
#define glDeleteVertexArrays imgl3wProcs.gl.DeleteVertexArrays
#define glDetachShader imgl3wProcs.gl.DetachShader
#define glDisable imgl3wProcs.gl.Disable
#define glDisableVertexAttribArray imgl3wProcs.gl.DisableVertexAttribArray
#define glDrawElements imgl3wProcs.gl.DrawElements
#define glDrawElementsBaseVertex imgl3wProcs.gl.DrawElementsBaseVertex
#define glEnable imgl3wProcs.gl.Enable
#define glEnableVertexAttribArray imgl3wProcs.gl.EnableVertexAttribArray
#define glFlush imgl3wProcs.gl.Flush
#define glGenBuffers imgl3wProcs.gl.GenBuffers
#define glGenTextures imgl3wProcs.gl.GenTextures
#define glGenVertexArrays imgl3wProcs.gl.GenVertexArrays
#define glGetAttribLocation imgl3wProcs.gl.GetAttribLocation
#define glGetError imgl3wProcs.gl.GetError
#define glGetIntegerv imgl3wProcs.gl.GetIntegerv
#define glGetProgramInfoLog imgl3wProcs.gl.GetProgramInfoLog
#define glGetProgramiv imgl3wProcs.gl.GetProgramiv
#define glGetShaderInfoLog imgl3wProcs.gl.GetShaderInfoLog
#define glGetShaderiv imgl3wProcs.gl.GetShaderiv
#define glGetString imgl3wProcs.gl.GetString
#define glGetStringi imgl3wProcs.gl.GetStringi
#define glGetUniformLocation imgl3wProcs.gl.GetUniformLocation
#define glGetVertexAttribPointerv imgl3wProcs.gl.GetVertexAttribPointerv
#define glGetVertexAttribiv imgl3wProcs.gl.GetVertexAttribiv
#define glIsEnabled imgl3wProcs.gl.IsEnabled
#define glLinkProgram imgl3wProcs.gl.LinkProgram
#define glPixelStorei imgl3wProcs.gl.PixelStorei
#define glPolygonMode imgl3wProcs.gl.PolygonMode
#define glReadPixels imgl3wProcs.gl.ReadPixels
#define glScissor imgl3wProcs.gl.Scissor
#define glShaderSource imgl3wProcs.gl.ShaderSource
#define glTexImage2D imgl3wProcs.gl.TexImage2D
#define glTexParameteri imgl3wProcs.gl.TexParameteri
#define glUniform1i imgl3wProcs.gl.Uniform1i
#define glUniformMatrix4fv imgl3wProcs.gl.UniformMatrix4fv
#define glUseProgram imgl3wProcs.gl.UseProgram
#define glVertexAttribPointer imgl3wProcs.gl.VertexAttribPointer
#define glViewport imgl3wProcs.gl.Viewport
#ifdef __cplusplus
}
#endif
#endif
#ifdef IMGL3W_IMPL
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#if defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#include <windows.h>
static HMODULE libgl;
typedef PROC(__stdcall* GL3WglGetProcAddr)(LPCSTR);
static GL3WglGetProcAddr wgl_get_proc_address;
static int open_libgl(void)
{
libgl = LoadLibraryA("opengl32.dll");
if (!libgl)
return GL3W_ERROR_LIBRARY_OPEN;
wgl_get_proc_address = (GL3WglGetProcAddr)GetProcAddress(libgl, "wglGetProcAddress");
return GL3W_OK;
}
static void close_libgl(void) { FreeLibrary(libgl); }
static GL3WglProc get_proc(const char *proc)
{
GL3WglProc res;
res = (GL3WglProc)wgl_get_proc_address(proc);
if (!res)
res = (GL3WglProc)GetProcAddress(libgl, proc);
return res;
}
#elif defined(__APPLE__)
#include <dlfcn.h>
static void *libgl;
static int open_libgl(void)
{
libgl = dlopen("/System/Library/Frameworks/OpenGL.framework/OpenGL", RTLD_LAZY | RTLD_LOCAL);
if (!libgl)
return GL3W_ERROR_LIBRARY_OPEN;
return GL3W_OK;
}
static void close_libgl(void) { dlclose(libgl); }
static GL3WglProc get_proc(const char *proc)
{
GL3WglProc res;
*(void **)(&res) = dlsym(libgl, proc);
return res;
}
#else
#include <dlfcn.h>
static void *libgl;
static GL3WglProc (*glx_get_proc_address)(const GLubyte *);
static int open_libgl(void)
{
libgl = dlopen("libGL.so.1", RTLD_LAZY | RTLD_LOCAL);
if (!libgl)
return GL3W_ERROR_LIBRARY_OPEN;
*(void **)(&glx_get_proc_address) = dlsym(libgl, "glXGetProcAddressARB");
return GL3W_OK;
}
static void close_libgl(void) { dlclose(libgl); }
static GL3WglProc get_proc(const char *proc)
{
GL3WglProc res;
res = glx_get_proc_address((const GLubyte *)proc);
if (!res)
*(void **)(&res) = dlsym(libgl, proc);
return res;
}
#endif
static struct { int major, minor; } version;
static int parse_version(void)
{
if (!glGetIntegerv)
return GL3W_ERROR_INIT;
glGetIntegerv(GL_MAJOR_VERSION, &version.major);
glGetIntegerv(GL_MINOR_VERSION, &version.minor);
if (version.major < 3)
return GL3W_ERROR_OPENGL_VERSION;
return GL3W_OK;
}
static void load_procs(GL3WGetProcAddressProc proc);
int imgl3wInit(void)
{
int res = open_libgl();
if (res)
return res;
atexit(close_libgl);
return imgl3wInit2(get_proc);
}
int imgl3wInit2(GL3WGetProcAddressProc proc)
{
load_procs(proc);
return parse_version();
}
int imgl3wIsSupported(int major, int minor)
{
if (major < 3)
return 0;
if (version.major == major)
return version.minor >= minor;
return version.major >= major;
}
GL3WglProc imgl3wGetProcAddress(const char *proc) { return get_proc(proc); }
static const char *proc_names[] = {
"glActiveTexture",
"glAttachShader",
"glBindBuffer",
"glBindSampler",
"glBindTexture",
"glBindVertexArray",
"glBlendEquation",
"glBlendEquationSeparate",
"glBlendFuncSeparate",
"glBufferData",
"glBufferSubData",
"glClear",
"glClearColor",
"glCompileShader",
"glCreateProgram",
"glCreateShader",
"glDeleteBuffers",
"glDeleteProgram",
"glDeleteShader",
"glDeleteTextures",
"glDeleteVertexArrays",
"glDetachShader",
"glDisable",
"glDisableVertexAttribArray",
"glDrawElements",
"glDrawElementsBaseVertex",
"glEnable",
"glEnableVertexAttribArray",
"glFlush",
"glGenBuffers",
"glGenTextures",
"glGenVertexArrays",
"glGetAttribLocation",
"glGetError",
"glGetIntegerv",
"glGetProgramInfoLog",
"glGetProgramiv",
"glGetShaderInfoLog",
"glGetShaderiv",
"glGetString",
"glGetStringi",
"glGetUniformLocation",
"glGetVertexAttribPointerv",
"glGetVertexAttribiv",
"glIsEnabled",
"glLinkProgram",
"glPixelStorei",
"glPolygonMode",
"glReadPixels",
"glScissor",
"glShaderSource",
"glTexImage2D",
"glTexParameteri",
"glUniform1i",
"glUniformMatrix4fv",
"glUseProgram",
"glVertexAttribPointer",
"glViewport",
};
GL3W_API union GL3WProcs imgl3wProcs;
static void load_procs(GL3WGetProcAddressProc proc)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(proc_names); i++)
imgl3wProcs.ptr[i] = proc(proc_names[i]);
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,25 +0,0 @@
// dear imgui: Platform Backend for OSX / Cocoa
// This needs to be used along with a Renderer (e.g. OpenGL2, OpenGL3, Vulkan, Metal..)
// [ALPHA] Early backend, not well tested. If you want a portable application, prefer using the GLFW or SDL platform Backends on Mac.
// Implemented features:
// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
// [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy kVK_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
// [X] Platform: OSX clipboard is supported within core Dear ImGui (no specific code in this backend).
// [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
// [X] Platform: IME support.
// [X] Platform: Multi-viewport / platform windows.
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
#include "imgui.h" // IMGUI_IMPL_API
@class NSEvent;
@class NSView;
IMGUI_IMPL_API bool ImGui_ImplOSX_Init(NSView* _Nonnull view);
IMGUI_IMPL_API void ImGui_ImplOSX_Shutdown();
IMGUI_IMPL_API void ImGui_ImplOSX_NewFrame(NSView* _Nullable view);

File diff suppressed because it is too large Load Diff

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