mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 12:21:18 +00:00
Added Daybreak, a system updater homebrew (#1073)
* Implemented a system updater homebrew (titled Daybreak) * git subrepo pull ./troposphere/daybreak/nanovg subrepo: subdir: "troposphere/daybreak/nanovg" merged: "c197ba2f" upstream: origin: "https://github.com/Adubbz/nanovg-deko.git" branch: "master" commit: "c197ba2f" git-subrepo: version: "0.4.1" origin: "???" commit: "???" (+1 squashed commits) Squashed commits: [232dc943] git subrepo clone https://github.com/Adubbz/nanovg-deko.git troposphere/daybreak/nanovg subrepo: subdir: "troposphere/daybreak/nanovg" merged: "52bb784b" upstream: origin: "https://github.com/Adubbz/nanovg-deko.git" branch: "master" commit: "52bb784b" git-subrepo: version: "0.4.1" origin: "???" commit: "???" * daybreak: switch to using hiddbg for home blocking (+1 squashed commits) Squashed commits: [4bfc7b0d] daybreak: block the home button during installation
This commit is contained in:
parent
b08ccd7341
commit
94eb2195d3
48 changed files with 24243 additions and 1 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -38,6 +38,9 @@
|
||||||
*.x86_64
|
*.x86_64
|
||||||
*.hex
|
*.hex
|
||||||
|
|
||||||
|
# Deko3d shaders
|
||||||
|
*.dksh
|
||||||
|
|
||||||
# Switch Executables
|
# Switch Executables
|
||||||
*.nso
|
*.nso
|
||||||
*.nro
|
*.nro
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
APPLICATIONS := reboot_to_payload
|
APPLICATIONS := daybreak reboot_to_payload
|
||||||
|
|
||||||
SUBFOLDERS := $(APPLICATIONS)
|
SUBFOLDERS := $(APPLICATIONS)
|
||||||
|
|
||||||
|
|
282
troposphere/daybreak/Makefile
Normal file
282
troposphere/daybreak/Makefile
Normal file
|
@ -0,0 +1,282 @@
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
.SUFFIXES:
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ifeq ($(strip $(DEVKITPRO)),)
|
||||||
|
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||||
|
endif
|
||||||
|
|
||||||
|
TOPDIR ?= $(CURDIR)
|
||||||
|
include $(DEVKITPRO)/libnx/switch_rules
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# TARGET is the name of the output
|
||||||
|
# BUILD is the directory where object files & intermediate files will be placed
|
||||||
|
# SOURCES is a list of directories containing source code
|
||||||
|
# DATA is a list of directories containing data files
|
||||||
|
# INCLUDES is a list of directories containing header files
|
||||||
|
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
|
||||||
|
#
|
||||||
|
# NO_ICON: if set to anything, do not use icon.
|
||||||
|
# NO_NACP: if set to anything, no .nacp file is generated.
|
||||||
|
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
|
||||||
|
# ICON is the filename of the icon (.jpg), relative to the project folder.
|
||||||
|
# If not set, it attempts to use one of the following (in this order):
|
||||||
|
# - <Project name>.jpg
|
||||||
|
# - icon.jpg
|
||||||
|
# - <libnx folder>/default_icon.jpg
|
||||||
|
#
|
||||||
|
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
|
||||||
|
# If not set, it attempts to use one of the following (in this order):
|
||||||
|
# - <Project name>.json
|
||||||
|
# - config.json
|
||||||
|
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
|
||||||
|
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
|
||||||
|
# NACP building is skipped as well.
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
TARGET := daybreak
|
||||||
|
BUILD := build
|
||||||
|
SOURCES := source nanovg/shaders
|
||||||
|
DATA := data
|
||||||
|
INCLUDES := include ../include
|
||||||
|
ROMFS := romfs
|
||||||
|
|
||||||
|
# Output folders for autogenerated files in romfs
|
||||||
|
OUT_SHADERS := shaders
|
||||||
|
|
||||||
|
APP_TITLE := Daybreak
|
||||||
|
APP_AUTHOR := Atmosphere-NX
|
||||||
|
APP_VERSION := 1.0.0
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# options for code generation
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||||
|
|
||||||
|
CFLAGS := -g -Wall -O2 -ffunction-sections \
|
||||||
|
$(ARCH) $(DEFINES)
|
||||||
|
|
||||||
|
CFLAGS += $(INCLUDE) -D__SWITCH__
|
||||||
|
|
||||||
|
CXXFLAGS := $(CFLAGS) -std=gnu++17 -fno-exceptions -fno-rtti
|
||||||
|
|
||||||
|
ASFLAGS := -g $(ARCH)
|
||||||
|
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||||
|
|
||||||
|
LIBS := -lnanovg -ldeko3d -lnx
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# list of directories containing libraries, this must be the top level containing
|
||||||
|
# include and lib
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
LIBDIRS := $(PORTLIBS) $(LIBNX) $(CURDIR)/nanovg/
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# no real need to edit anything past this point unless you need to add additional
|
||||||
|
# rules for different file extensions
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||||
|
export TOPDIR := $(CURDIR)
|
||||||
|
|
||||||
|
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||||
|
|
||||||
|
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
|
SUBFOLDERS := nanovg
|
||||||
|
|
||||||
|
TOPTARGETS := all clean
|
||||||
|
|
||||||
|
$(TOPTARGETS): $(SUBFOLDERS)
|
||||||
|
|
||||||
|
$(SUBFOLDERS):
|
||||||
|
$(MAKE) -C $@ $(MAKECMDGOALS)
|
||||||
|
|
||||||
|
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||||
|
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||||
|
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||||
|
GLSLFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.glsl)))
|
||||||
|
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# use CXX for linking C++ projects, CC for standard C
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifeq ($(strip $(CPPFILES)),)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CC)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CXX)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||||
|
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||||
|
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
|
||||||
|
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||||
|
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||||
|
|
||||||
|
ifneq ($(strip $(ROMFS)),)
|
||||||
|
ROMFS_TARGETS :=
|
||||||
|
ROMFS_FOLDERS :=
|
||||||
|
ifneq ($(strip $(OUT_SHADERS)),)
|
||||||
|
ROMFS_SHADERS := $(ROMFS)/$(OUT_SHADERS)
|
||||||
|
ROMFS_TARGETS += $(patsubst %.glsl, $(ROMFS_SHADERS)/%.dksh, $(GLSLFILES))
|
||||||
|
ROMFS_FOLDERS += $(ROMFS_SHADERS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
export ROMFS_DEPS := $(foreach file,$(ROMFS_TARGETS),$(CURDIR)/$(file))
|
||||||
|
endif
|
||||||
|
|
||||||
|
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||||
|
-I$(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
|
ifeq ($(strip $(CONFIG_JSON)),)
|
||||||
|
jsons := $(wildcard *.json)
|
||||||
|
ifneq (,$(findstring $(TARGET).json,$(jsons)))
|
||||||
|
export APP_JSON := $(TOPDIR)/$(TARGET).json
|
||||||
|
else
|
||||||
|
ifneq (,$(findstring config.json,$(jsons)))
|
||||||
|
export APP_JSON := $(TOPDIR)/config.json
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(ICON)),)
|
||||||
|
icons := $(wildcard *.jpg)
|
||||||
|
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
|
||||||
|
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
|
||||||
|
else
|
||||||
|
ifneq (,$(findstring icon.jpg,$(icons)))
|
||||||
|
export APP_ICON := $(TOPDIR)/icon.jpg
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
export APP_ICON := $(TOPDIR)/$(ICON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_ICON)),)
|
||||||
|
export NROFLAGS += --icon=$(APP_ICON)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_NACP)),)
|
||||||
|
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(APP_TITLEID),)
|
||||||
|
export NACPFLAGS += --titleid=$(APP_TITLEID)
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq ($(ROMFS),)
|
||||||
|
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: $(TOPTARGETS) $(SUBFOLDERS) all clean
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
all: $(ROMFS_TARGETS) | $(BUILD)
|
||||||
|
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||||
|
|
||||||
|
$(BUILD):
|
||||||
|
@mkdir -p $@
|
||||||
|
|
||||||
|
ifneq ($(strip $(ROMFS_TARGETS)),)
|
||||||
|
|
||||||
|
$(ROMFS_TARGETS): | $(ROMFS_FOLDERS)
|
||||||
|
|
||||||
|
$(ROMFS_FOLDERS):
|
||||||
|
@mkdir -p $@
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%_vsh.dksh: %_vsh.glsl
|
||||||
|
@echo {vert} $(notdir $<)
|
||||||
|
@uam -s vert -o $@ $<
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%_tcsh.dksh: %_tcsh.glsl
|
||||||
|
@echo {tess_ctrl} $(notdir $<)
|
||||||
|
@uam -s tess_ctrl -o $@ $<
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%_tesh.dksh: %_tesh.glsl
|
||||||
|
@echo {tess_eval} $(notdir $<)
|
||||||
|
@uam -s tess_eval -o $@ $<
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%_gsh.dksh: %_gsh.glsl
|
||||||
|
@echo {geom} $(notdir $<)
|
||||||
|
@uam -s geom -o $@ $<
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%_fsh.dksh: %_fsh.glsl
|
||||||
|
@echo {frag} $(notdir $<)
|
||||||
|
@uam -s frag -o $@ $<
|
||||||
|
|
||||||
|
$(ROMFS_SHADERS)/%.dksh: %.glsl
|
||||||
|
@echo {comp} $(notdir $<)
|
||||||
|
@uam -s comp -o $@ $<
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
clean:
|
||||||
|
@echo clean ...
|
||||||
|
ifeq ($(strip $(APP_JSON)),)
|
||||||
|
@rm -fr $(BUILD) $(ROMFS_FOLDERS) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
|
||||||
|
else
|
||||||
|
@rm -fr $(BUILD) $(ROMFS_FOLDERS) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
.PHONY: all
|
||||||
|
|
||||||
|
DEPENDS := $(OFILES:.o=.d)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# main targets
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifeq ($(strip $(APP_JSON)),)
|
||||||
|
|
||||||
|
all : $(OUTPUT).nro
|
||||||
|
|
||||||
|
ifeq ($(strip $(NO_NACP)),)
|
||||||
|
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp $(ROMFS_DEPS)
|
||||||
|
else
|
||||||
|
$(OUTPUT).nro : $(OUTPUT).elf $(ROMFS_DEPS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
all : $(OUTPUT).nsp
|
||||||
|
|
||||||
|
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
|
||||||
|
|
||||||
|
$(OUTPUT).nso : $(OUTPUT).elf
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(OUTPUT).elf : $(OFILES)
|
||||||
|
|
||||||
|
$(OFILES_SRC) : $(HFILES_BIN)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# you need a rule like this for each extension you use as binary data
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.bin.o %_bin.h : %.bin
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@$(bin2o)
|
||||||
|
|
||||||
|
-include $(DEPENDS)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------------
|
BIN
troposphere/daybreak/icon.jpg
Normal file
BIN
troposphere/daybreak/icon.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
93
troposphere/daybreak/nanovg/.gitignore
vendored
Normal file
93
troposphere/daybreak/nanovg/.gitignore
vendored
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
# deko3d shaders
|
||||||
|
*.dksh
|
||||||
|
|
||||||
|
# Prerequisites
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# Object files
|
||||||
|
*.o
|
||||||
|
*.ko
|
||||||
|
*.obj
|
||||||
|
*.elf
|
||||||
|
|
||||||
|
# Linker output
|
||||||
|
*.ilk
|
||||||
|
*.map
|
||||||
|
*.exp
|
||||||
|
*.lst
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Libraries
|
||||||
|
*.lib
|
||||||
|
*.a
|
||||||
|
*.la
|
||||||
|
*.lo
|
||||||
|
|
||||||
|
# Shared objects (inc. Windows DLLs)
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.lz4
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
*.i*86
|
||||||
|
*.x86_64
|
||||||
|
*.hex
|
||||||
|
|
||||||
|
# Switch Executables
|
||||||
|
*.nso
|
||||||
|
*.nro
|
||||||
|
*.nacp
|
||||||
|
*.npdm
|
||||||
|
*.pfs0
|
||||||
|
*.nsp
|
||||||
|
*.kip
|
||||||
|
|
||||||
|
# Debug files
|
||||||
|
*.dSYM/
|
||||||
|
*.su
|
||||||
|
*.idb
|
||||||
|
*.pdb
|
||||||
|
|
||||||
|
# Kernel Module Compile Results
|
||||||
|
*.mod*
|
||||||
|
*.cmd
|
||||||
|
.tmp_versions/
|
||||||
|
modules.order
|
||||||
|
Module.symvers
|
||||||
|
Mkfile.old
|
||||||
|
dkms.conf
|
||||||
|
|
||||||
|
# Distribution files
|
||||||
|
*.tgz
|
||||||
|
*.zip
|
||||||
|
*.bz2
|
||||||
|
|
||||||
|
# IDA binaries
|
||||||
|
*.id0
|
||||||
|
*.id1
|
||||||
|
*.id2
|
||||||
|
*.idb
|
||||||
|
*.i64
|
||||||
|
*.nam
|
||||||
|
*.til
|
||||||
|
|
||||||
|
# KEYS file for sept-secondary.
|
||||||
|
*.pyc
|
||||||
|
sept/sept-secondary/KEYS.py
|
||||||
|
|
||||||
|
.**/
|
||||||
|
|
||||||
|
# NOTE: make sure to make exceptions to this pattern when needed!
|
||||||
|
*.bin
|
||||||
|
*.enc
|
||||||
|
|
||||||
|
**/out
|
||||||
|
**/build
|
12
troposphere/daybreak/nanovg/.gitrepo
Normal file
12
troposphere/daybreak/nanovg/.gitrepo
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
; DO NOT EDIT (unless you know what you are doing)
|
||||||
|
;
|
||||||
|
; This subdirectory is a git "subrepo", and this file is maintained by the
|
||||||
|
; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
|
||||||
|
;
|
||||||
|
[subrepo]
|
||||||
|
remote = https://github.com/Adubbz/nanovg-deko.git
|
||||||
|
branch = master
|
||||||
|
commit = c197ba2f0d1fe2c70e2d49c61f16b4063aef569e
|
||||||
|
parent = 171d97e0ac2eaf1bca1c48727404fec45621f1fd
|
||||||
|
method = merge
|
||||||
|
cmdver = 0.4.1
|
18
troposphere/daybreak/nanovg/LICENSE
Normal file
18
troposphere/daybreak/nanovg/LICENSE
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
Copyright (c) 2020 Adubbz, Mikko Mononen memon@inside.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.
|
168
troposphere/daybreak/nanovg/Makefile
Normal file
168
troposphere/daybreak/nanovg/Makefile
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
.SUFFIXES:
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ifeq ($(strip $(DEVKITPRO)),)
|
||||||
|
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||||
|
endif
|
||||||
|
|
||||||
|
TOPDIR ?= $(CURDIR)
|
||||||
|
include $(DEVKITPRO)/libnx/switch_rules
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# TARGET is the name of the output
|
||||||
|
# BUILD is the directory where object files & intermediate files will be placed
|
||||||
|
# SOURCES is a list of directories containing source code
|
||||||
|
# DATA is a list of directories containing data files
|
||||||
|
# INCLUDES is a list of directories containing header files
|
||||||
|
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
|
||||||
|
#
|
||||||
|
# NO_ICON: if set to anything, do not use icon.
|
||||||
|
# NO_NACP: if set to anything, no .nacp file is generated.
|
||||||
|
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
|
||||||
|
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
|
||||||
|
# ICON is the filename of the icon (.jpg), relative to the project folder.
|
||||||
|
# If not set, it attempts to use one of the following (in this order):
|
||||||
|
# - <Project name>.jpg
|
||||||
|
# - icon.jpg
|
||||||
|
# - <libnx folder>/default_icon.jpg
|
||||||
|
#
|
||||||
|
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
|
||||||
|
# If not set, it attempts to use one of the following (in this order):
|
||||||
|
# - <Project name>.json
|
||||||
|
# - config.json
|
||||||
|
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
|
||||||
|
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
|
||||||
|
# NACP building is skipped as well.
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
TARGET := libnanovg
|
||||||
|
BUILD := build
|
||||||
|
SOURCES := source source/framework
|
||||||
|
INCLUDES := include include/nanovg include/nanovg/framework
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# options for code generation
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
|
||||||
|
|
||||||
|
CFLAGS := -g -Wall -O2 -ffunction-sections \
|
||||||
|
$(ARCH) $(DEFINES)
|
||||||
|
|
||||||
|
CFLAGS += $(INCLUDE) -D__SWITCH__
|
||||||
|
|
||||||
|
CXXFLAGS := $(CFLAGS) -std=gnu++17 -fno-exceptions -fno-rtti
|
||||||
|
|
||||||
|
ASFLAGS := -g $(ARCH)
|
||||||
|
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||||
|
|
||||||
|
#LIBS := -ldeko3dd -lglad -lEGL -lglapi -ldrm_nouveau
|
||||||
|
LIBS := -ldeko3d
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# list of directories containing libraries, this must be the top level containing
|
||||||
|
# include and lib
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
LIBDIRS := $(PORTLIBS) $(LIBNX)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# no real need to edit anything past this point unless you need to add additional
|
||||||
|
# rules for different file extensions
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||||
|
export TOPDIR := $(CURDIR)
|
||||||
|
|
||||||
|
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||||
|
|
||||||
|
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
|
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||||
|
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||||
|
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||||
|
GLSLFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.glsl)))
|
||||||
|
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# use CXX for linking C++ projects, CC for standard C
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifeq ($(strip $(CPPFILES)),)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CC)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CXX)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||||
|
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||||
|
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
|
||||||
|
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||||
|
|
||||||
|
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||||
|
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||||
|
-I$(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
all: lib/$(TARGET).a
|
||||||
|
|
||||||
|
lib:
|
||||||
|
@[ -d $@ ] || mkdir -p $@
|
||||||
|
|
||||||
|
release:
|
||||||
|
@[ -d $@ ] || mkdir -p $@
|
||||||
|
|
||||||
|
lib/$(TARGET).a : lib release $(SOURCES) $(INCLUDES)
|
||||||
|
@$(MAKE) BUILD=release OUTPUT=$(CURDIR)/$@ \
|
||||||
|
BUILD_CFLAGS="-DNDEBUG=1 -O2" \
|
||||||
|
DEPSDIR=$(CURDIR)/release \
|
||||||
|
--no-print-directory -C release \
|
||||||
|
-f $(CURDIR)/Makefile
|
||||||
|
|
||||||
|
dist-bin: all
|
||||||
|
@tar --exclude=*~ -cjf $(TARGET).tar.bz2 include lib
|
||||||
|
|
||||||
|
dist-src:
|
||||||
|
@tar --exclude=*~ -cjf $(TARGET)-src.tar.bz2 include source Makefile
|
||||||
|
|
||||||
|
dist: dist-src dist-bin
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
clean:
|
||||||
|
@echo clean ...
|
||||||
|
@rm -fr release lib *.bz2
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
|
||||||
|
DEPENDS := $(OFILES:.o=.d)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# main targets
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
$(OUTPUT) : $(OFILES)
|
||||||
|
|
||||||
|
$(OFILES) : $(GCH_FILES)
|
||||||
|
|
||||||
|
$(OFILES_SRC) : $(HFILES_BIN)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# you need a rule like this for each extension you use as binary data
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.bin.o %_bin.h : %.bin
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
@echo $(notdir $<)
|
||||||
|
@$(bin2o)
|
||||||
|
|
||||||
|
-include $(DEPENDS)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------------
|
18
troposphere/daybreak/nanovg/README.md
Normal file
18
troposphere/daybreak/nanovg/README.md
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
NanoVG for Deko3D
|
||||||
|
==========
|
||||||
|
|
||||||
|
NanoVG is small antialiased vector graphics rendering library. This is a port to [deko3d](https://github.com/devkitPro/deko3d), a low level 3D graphics API targetting the Nvidia Tegra X1 found inside the Nintendo Switch.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
An example of using this library can be found [here](https://github.com/Adubbz/nanovg-deko3d-example).
|
||||||
|
|
||||||
|
## License
|
||||||
|
The library is licensed under [zlib license](LICENSE).
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
- fincs' deko3d framework is licensed under [zlib license](source/framework/LICENSE).
|
||||||
|
|
||||||
|
## Links
|
||||||
|
The original [nanovg project](https://github.com/memononen/nanovg).
|
||||||
|
Uses [stb_truetype](http://nothings.org) for font rendering.
|
||||||
|
Uses [stb_image](http://nothings.org) for image loading.
|
697
troposphere/daybreak/nanovg/include/nanovg.h
Normal file
697
troposphere/daybreak/nanovg/include/nanovg.h
Normal file
|
@ -0,0 +1,697 @@
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013 Mikko Mononen memon@inside.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 NANOVG_H
|
||||||
|
#define NANOVG_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NVG_PI 3.14159265358979323846264338327f
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct NVGcontext NVGcontext;
|
||||||
|
|
||||||
|
struct NVGcolor {
|
||||||
|
union {
|
||||||
|
float rgba[4];
|
||||||
|
struct {
|
||||||
|
float r,g,b,a;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
typedef struct NVGcolor NVGcolor;
|
||||||
|
|
||||||
|
struct NVGpaint {
|
||||||
|
float xform[6];
|
||||||
|
float extent[2];
|
||||||
|
float radius;
|
||||||
|
float feather;
|
||||||
|
NVGcolor innerColor;
|
||||||
|
NVGcolor outerColor;
|
||||||
|
int image;
|
||||||
|
};
|
||||||
|
typedef struct NVGpaint NVGpaint;
|
||||||
|
|
||||||
|
enum NVGwinding {
|
||||||
|
NVG_CCW = 1, // Winding for solid shapes
|
||||||
|
NVG_CW = 2, // Winding for holes
|
||||||
|
};
|
||||||
|
|
||||||
|
enum NVGsolidity {
|
||||||
|
NVG_SOLID = 1, // CCW
|
||||||
|
NVG_HOLE = 2, // CW
|
||||||
|
};
|
||||||
|
|
||||||
|
enum NVGlineCap {
|
||||||
|
NVG_BUTT,
|
||||||
|
NVG_ROUND,
|
||||||
|
NVG_SQUARE,
|
||||||
|
NVG_BEVEL,
|
||||||
|
NVG_MITER,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum NVGalign {
|
||||||
|
// Horizontal align
|
||||||
|
NVG_ALIGN_LEFT = 1<<0, // Default, align text horizontally to left.
|
||||||
|
NVG_ALIGN_CENTER = 1<<1, // Align text horizontally to center.
|
||||||
|
NVG_ALIGN_RIGHT = 1<<2, // Align text horizontally to right.
|
||||||
|
// Vertical align
|
||||||
|
NVG_ALIGN_TOP = 1<<3, // Align text vertically to top.
|
||||||
|
NVG_ALIGN_MIDDLE = 1<<4, // Align text vertically to middle.
|
||||||
|
NVG_ALIGN_BOTTOM = 1<<5, // Align text vertically to bottom.
|
||||||
|
NVG_ALIGN_BASELINE = 1<<6, // Default, align text vertically to baseline.
|
||||||
|
};
|
||||||
|
|
||||||
|
enum NVGblendFactor {
|
||||||
|
NVG_ZERO = 1<<0,
|
||||||
|
NVG_ONE = 1<<1,
|
||||||
|
NVG_SRC_COLOR = 1<<2,
|
||||||
|
NVG_ONE_MINUS_SRC_COLOR = 1<<3,
|
||||||
|
NVG_DST_COLOR = 1<<4,
|
||||||
|
NVG_ONE_MINUS_DST_COLOR = 1<<5,
|
||||||
|
NVG_SRC_ALPHA = 1<<6,
|
||||||
|
NVG_ONE_MINUS_SRC_ALPHA = 1<<7,
|
||||||
|
NVG_DST_ALPHA = 1<<8,
|
||||||
|
NVG_ONE_MINUS_DST_ALPHA = 1<<9,
|
||||||
|
NVG_SRC_ALPHA_SATURATE = 1<<10,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum NVGcompositeOperation {
|
||||||
|
NVG_SOURCE_OVER,
|
||||||
|
NVG_SOURCE_IN,
|
||||||
|
NVG_SOURCE_OUT,
|
||||||
|
NVG_ATOP,
|
||||||
|
NVG_DESTINATION_OVER,
|
||||||
|
NVG_DESTINATION_IN,
|
||||||
|
NVG_DESTINATION_OUT,
|
||||||
|
NVG_DESTINATION_ATOP,
|
||||||
|
NVG_LIGHTER,
|
||||||
|
NVG_COPY,
|
||||||
|
NVG_XOR,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NVGcompositeOperationState {
|
||||||
|
int srcRGB;
|
||||||
|
int dstRGB;
|
||||||
|
int srcAlpha;
|
||||||
|
int dstAlpha;
|
||||||
|
};
|
||||||
|
typedef struct NVGcompositeOperationState NVGcompositeOperationState;
|
||||||
|
|
||||||
|
struct NVGglyphPosition {
|
||||||
|
const char* str; // Position of the glyph in the input string.
|
||||||
|
float x; // The x-coordinate of the logical glyph position.
|
||||||
|
float minx, maxx; // The bounds of the glyph shape.
|
||||||
|
};
|
||||||
|
typedef struct NVGglyphPosition NVGglyphPosition;
|
||||||
|
|
||||||
|
struct NVGtextRow {
|
||||||
|
const char* start; // Pointer to the input text where the row starts.
|
||||||
|
const char* end; // Pointer to the input text where the row ends (one past the last character).
|
||||||
|
const char* next; // Pointer to the beginning of the next row.
|
||||||
|
float width; // Logical width of the row.
|
||||||
|
float minx, maxx; // Actual bounds of the row. Logical with and bounds can differ because of kerning and some parts over extending.
|
||||||
|
};
|
||||||
|
typedef struct NVGtextRow NVGtextRow;
|
||||||
|
|
||||||
|
enum NVGimageFlags {
|
||||||
|
NVG_IMAGE_GENERATE_MIPMAPS = 1<<0, // Generate mipmaps during creation of the image.
|
||||||
|
NVG_IMAGE_REPEATX = 1<<1, // Repeat image in X direction.
|
||||||
|
NVG_IMAGE_REPEATY = 1<<2, // Repeat image in Y direction.
|
||||||
|
NVG_IMAGE_FLIPY = 1<<3, // Flips (inverses) image in Y direction when rendered.
|
||||||
|
NVG_IMAGE_PREMULTIPLIED = 1<<4, // Image data has premultiplied alpha.
|
||||||
|
NVG_IMAGE_NEAREST = 1<<5, // Image interpolation is Nearest instead Linear
|
||||||
|
};
|
||||||
|
|
||||||
|
// Begin drawing a new frame
|
||||||
|
// Calls to nanovg drawing API should be wrapped in nvgBeginFrame() & nvgEndFrame()
|
||||||
|
// nvgBeginFrame() defines the size of the window to render to in relation currently
|
||||||
|
// set viewport (i.e. glViewport on GL backends). Device pixel ration allows to
|
||||||
|
// control the rendering on Hi-DPI devices.
|
||||||
|
// For example, GLFW returns two dimension for an opened window: window size and
|
||||||
|
// frame buffer size. In that case you would set windowWidth/Height to the window size
|
||||||
|
// devicePixelRatio to: frameBufferWidth / windowWidth.
|
||||||
|
void nvgBeginFrame(NVGcontext* ctx, float windowWidth, float windowHeight, float devicePixelRatio);
|
||||||
|
|
||||||
|
// Cancels drawing the current frame.
|
||||||
|
void nvgCancelFrame(NVGcontext* ctx);
|
||||||
|
|
||||||
|
// Ends drawing flushing remaining render state.
|
||||||
|
void nvgEndFrame(NVGcontext* ctx);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Composite operation
|
||||||
|
//
|
||||||
|
// The composite operations in NanoVG are modeled after HTML Canvas API, and
|
||||||
|
// the blend func is based on OpenGL (see corresponding manuals for more info).
|
||||||
|
// The colors in the blending state have premultiplied alpha.
|
||||||
|
|
||||||
|
// Sets the composite operation. The op parameter should be one of NVGcompositeOperation.
|
||||||
|
void nvgGlobalCompositeOperation(NVGcontext* ctx, int op);
|
||||||
|
|
||||||
|
// Sets the composite operation with custom pixel arithmetic. The parameters should be one of NVGblendFactor.
|
||||||
|
void nvgGlobalCompositeBlendFunc(NVGcontext* ctx, int sfactor, int dfactor);
|
||||||
|
|
||||||
|
// Sets the composite operation with custom pixel arithmetic for RGB and alpha components separately. The parameters should be one of NVGblendFactor.
|
||||||
|
void nvgGlobalCompositeBlendFuncSeparate(NVGcontext* ctx, int srcRGB, int dstRGB, int srcAlpha, int dstAlpha);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Color utils
|
||||||
|
//
|
||||||
|
// Colors in NanoVG are stored as unsigned ints in ABGR format.
|
||||||
|
|
||||||
|
// Returns a color value from red, green, blue values. Alpha will be set to 255 (1.0f).
|
||||||
|
NVGcolor nvgRGB(unsigned char r, unsigned char g, unsigned char b);
|
||||||
|
|
||||||
|
// Returns a color value from red, green, blue values. Alpha will be set to 1.0f.
|
||||||
|
NVGcolor nvgRGBf(float r, float g, float b);
|
||||||
|
|
||||||
|
|
||||||
|
// Returns a color value from red, green, blue and alpha values.
|
||||||
|
NVGcolor nvgRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
|
||||||
|
|
||||||
|
// Returns a color value from red, green, blue and alpha values.
|
||||||
|
NVGcolor nvgRGBAf(float r, float g, float b, float a);
|
||||||
|
|
||||||
|
|
||||||
|
// Linearly interpolates from color c0 to c1, and returns resulting color value.
|
||||||
|
NVGcolor nvgLerpRGBA(NVGcolor c0, NVGcolor c1, float u);
|
||||||
|
|
||||||
|
// Sets transparency of a color value.
|
||||||
|
NVGcolor nvgTransRGBA(NVGcolor c0, unsigned char a);
|
||||||
|
|
||||||
|
// Sets transparency of a color value.
|
||||||
|
NVGcolor nvgTransRGBAf(NVGcolor c0, float a);
|
||||||
|
|
||||||
|
// Returns color value specified by hue, saturation and lightness.
|
||||||
|
// HSL values are all in range [0..1], alpha will be set to 255.
|
||||||
|
NVGcolor nvgHSL(float h, float s, float l);
|
||||||
|
|
||||||
|
// Returns color value specified by hue, saturation and lightness and alpha.
|
||||||
|
// HSL values are all in range [0..1], alpha in range [0..255]
|
||||||
|
NVGcolor nvgHSLA(float h, float s, float l, unsigned char a);
|
||||||
|
|
||||||
|
//
|
||||||
|
// State Handling
|
||||||
|
//
|
||||||
|
// NanoVG contains state which represents how paths will be rendered.
|
||||||
|
// The state contains transform, fill and stroke styles, text and font styles,
|
||||||
|
// and scissor clipping.
|
||||||
|
|
||||||
|
// Pushes and saves the current render state into a state stack.
|
||||||
|
// A matching nvgRestore() must be used to restore the state.
|
||||||
|
void nvgSave(NVGcontext* ctx);
|
||||||
|
|
||||||
|
// Pops and restores current render state.
|
||||||
|
void nvgRestore(NVGcontext* ctx);
|
||||||
|
|
||||||
|
// Resets current render state to default values. Does not affect the render state stack.
|
||||||
|
void nvgReset(NVGcontext* ctx);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Render styles
|
||||||
|
//
|
||||||
|
// Fill and stroke render style can be either a solid color or a paint which is a gradient or a pattern.
|
||||||
|
// Solid color is simply defined as a color value, different kinds of paints can be created
|
||||||
|
// using nvgLinearGradient(), nvgBoxGradient(), nvgRadialGradient() and nvgImagePattern().
|
||||||
|
//
|
||||||
|
// Current render style can be saved and restored using nvgSave() and nvgRestore().
|
||||||
|
|
||||||
|
// Sets whether to draw antialias for nvgStroke() and nvgFill(). It's enabled by default.
|
||||||
|
void nvgShapeAntiAlias(NVGcontext* ctx, int enabled);
|
||||||
|
|
||||||
|
// Sets current stroke style to a solid color.
|
||||||
|
void nvgStrokeColor(NVGcontext* ctx, NVGcolor color);
|
||||||
|
|
||||||
|
// Sets current stroke style to a paint, which can be a one of the gradients or a pattern.
|
||||||
|
void nvgStrokePaint(NVGcontext* ctx, NVGpaint paint);
|
||||||
|
|
||||||
|
// Sets current fill style to a solid color.
|
||||||
|
void nvgFillColor(NVGcontext* ctx, NVGcolor color);
|
||||||
|
|
||||||
|
// Sets current fill style to a paint, which can be a one of the gradients or a pattern.
|
||||||
|
void nvgFillPaint(NVGcontext* ctx, NVGpaint paint);
|
||||||
|
|
||||||
|
// Sets the miter limit of the stroke style.
|
||||||
|
// Miter limit controls when a sharp corner is beveled.
|
||||||
|
void nvgMiterLimit(NVGcontext* ctx, float limit);
|
||||||
|
|
||||||
|
// Sets the stroke width of the stroke style.
|
||||||
|
void nvgStrokeWidth(NVGcontext* ctx, float size);
|
||||||
|
|
||||||
|
// Sets how the end of the line (cap) is drawn,
|
||||||
|
// Can be one of: NVG_BUTT (default), NVG_ROUND, NVG_SQUARE.
|
||||||
|
void nvgLineCap(NVGcontext* ctx, int cap);
|
||||||
|
|
||||||
|
// Sets how sharp path corners are drawn.
|
||||||
|
// Can be one of NVG_MITER (default), NVG_ROUND, NVG_BEVEL.
|
||||||
|
void nvgLineJoin(NVGcontext* ctx, int join);
|
||||||
|
|
||||||
|
// Sets the transparency applied to all rendered shapes.
|
||||||
|
// Already transparent paths will get proportionally more transparent as well.
|
||||||
|
void nvgGlobalAlpha(NVGcontext* ctx, float alpha);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Transforms
|
||||||
|
//
|
||||||
|
// The paths, gradients, patterns and scissor region are transformed by an transformation
|
||||||
|
// matrix at the time when they are passed to the API.
|
||||||
|
// The current transformation matrix is a affine matrix:
|
||||||
|
// [sx kx tx]
|
||||||
|
// [ky sy ty]
|
||||||
|
// [ 0 0 1]
|
||||||
|
// Where: sx,sy define scaling, kx,ky skewing, and tx,ty translation.
|
||||||
|
// The last row is assumed to be 0,0,1 and is not stored.
|
||||||
|
//
|
||||||
|
// Apart from nvgResetTransform(), each transformation function first creates
|
||||||
|
// specific transformation matrix and pre-multiplies the current transformation by it.
|
||||||
|
//
|
||||||
|
// Current coordinate system (transformation) can be saved and restored using nvgSave() and nvgRestore().
|
||||||
|
|
||||||
|
// Resets current transform to a identity matrix.
|
||||||
|
void nvgResetTransform(NVGcontext* ctx);
|
||||||
|
|
||||||
|
// Premultiplies current coordinate system by specified matrix.
|
||||||
|
// The parameters are interpreted as matrix as follows:
|
||||||
|
// [a c e]
|
||||||
|
// [b d f]
|
||||||
|
// [0 0 1]
|
||||||
|
void nvgTransform(NVGcontext* ctx, float a, float b, float c, float d, float e, float f);
|
||||||
|
|
||||||
|
// Translates current coordinate system.
|
||||||
|
void nvgTranslate(NVGcontext* ctx, float x, float y);
|
||||||
|
|
||||||
|
// Rotates current coordinate system. Angle is specified in radians.
|
||||||
|
void nvgRotate(NVGcontext* ctx, float angle);
|
||||||
|
|
||||||
|
// Skews the current coordinate system along X axis. Angle is specified in radians.
|
||||||
|
void nvgSkewX(NVGcontext* ctx, float angle);
|
||||||
|
|
||||||
|
// Skews the current coordinate system along Y axis. Angle is specified in radians.
|
||||||
|
void nvgSkewY(NVGcontext* ctx, float angle);
|
||||||
|
|
||||||
|
// Scales the current coordinate system.
|
||||||
|
void nvgScale(NVGcontext* ctx, float x, float y);
|
||||||
|
|
||||||
|
// Stores the top part (a-f) of the current transformation matrix in to the specified buffer.
|
||||||
|
// [a c e]
|
||||||
|
// [b d f]
|
||||||
|
// [0 0 1]
|
||||||
|
// There should be space for 6 floats in the return buffer for the values a-f.
|
||||||
|
void nvgCurrentTransform(NVGcontext* ctx, float* xform);
|
||||||
|
|
||||||
|
|
||||||
|
// The following functions can be used to make calculations on 2x3 transformation matrices.
|
||||||
|
// A 2x3 matrix is represented as float[6].
|
||||||
|
|
||||||
|
// Sets the transform to identity matrix.
|
||||||
|
void nvgTransformIdentity(float* dst);
|
||||||
|
|
||||||
|
// Sets the transform to translation matrix matrix.
|
||||||
|
void nvgTransformTranslate(float* dst, float tx, float ty);
|
||||||
|
|
||||||
|
// Sets the transform to scale matrix.
|
||||||
|
void nvgTransformScale(float* dst, float sx, float sy);
|
||||||
|
|
||||||
|
// Sets the transform to rotate matrix. Angle is specified in radians.
|
||||||
|
void nvgTransformRotate(float* dst, float a);
|
||||||
|
|
||||||
|
// Sets the transform to skew-x matrix. Angle is specified in radians.
|
||||||
|
void nvgTransformSkewX(float* dst, float a);
|
||||||
|
|
||||||
|
// Sets the transform to skew-y matrix. Angle is specified in radians.
|
||||||
|
void nvgTransformSkewY(float* dst, float a);
|
||||||
|
|
||||||
|
// Sets the transform to the result of multiplication of two transforms, of A = A*B.
|
||||||
|
void nvgTransformMultiply(float* dst, const float* src);
|
||||||
|
|
||||||
|
// Sets the transform to the result of multiplication of two transforms, of A = B*A.
|
||||||
|
void nvgTransformPremultiply(float* dst, const float* src);
|
||||||
|
|
||||||
|
// Sets the destination to inverse of specified transform.
|
||||||
|
// Returns 1 if the inverse could be calculated, else 0.
|
||||||
|
int nvgTransformInverse(float* dst, const float* src);
|
||||||
|
|
||||||
|
// Transform a point by given transform.
|
||||||
|
void nvgTransformPoint(float* dstx, float* dsty, const float* xform, float srcx, float srcy);
|
||||||
|
|
||||||
|
// Converts degrees to radians and vice versa.
|
||||||
|
float nvgDegToRad(float deg);
|
||||||
|
float nvgRadToDeg(float rad);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Images
|
||||||
|
//
|
||||||
|
// NanoVG allows you to load jpg, png, psd, tga, pic and gif files to be used for rendering.
|
||||||
|
// In addition you can upload your own image. The image loading is provided by stb_image.
|
||||||
|
// The parameter imageFlags is combination of flags defined in NVGimageFlags.
|
||||||
|
|
||||||
|
// Creates image by loading it from the disk from specified file name.
|
||||||
|
// Returns handle to the image.
|
||||||
|
int nvgCreateImage(NVGcontext* ctx, const char* filename, int imageFlags);
|
||||||
|
|
||||||
|
// Creates image by loading it from the specified chunk of memory.
|
||||||
|
// Returns handle to the image.
|
||||||
|
int nvgCreateImageMem(NVGcontext* ctx, int imageFlags, unsigned char* data, int ndata);
|
||||||
|
|
||||||
|
// Creates image from specified image data.
|
||||||
|
// Returns handle to the image.
|
||||||
|
int nvgCreateImageRGBA(NVGcontext* ctx, int w, int h, int imageFlags, const unsigned char* data);
|
||||||
|
|
||||||
|
// Updates image data specified by image handle.
|
||||||
|
void nvgUpdateImage(NVGcontext* ctx, int image, const unsigned char* data);
|
||||||
|
|
||||||
|
// Returns the dimensions of a created image.
|
||||||
|
void nvgImageSize(NVGcontext* ctx, int image, int* w, int* h);
|
||||||
|
|
||||||
|
// Deletes created image.
|
||||||
|
void nvgDeleteImage(NVGcontext* ctx, int image);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Paints
|
||||||
|
//
|
||||||
|
// NanoVG supports four types of paints: linear gradient, box gradient, radial gradient and image pattern.
|
||||||
|
// These can be used as paints for strokes and fills.
|
||||||
|
|
||||||
|
// Creates and returns a linear gradient. Parameters (sx,sy)-(ex,ey) specify the start and end coordinates
|
||||||
|
// of the linear gradient, icol specifies the start color and ocol the end color.
|
||||||
|
// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint().
|
||||||
|
NVGpaint nvgLinearGradient(NVGcontext* ctx, float sx, float sy, float ex, float ey,
|
||||||
|
NVGcolor icol, NVGcolor ocol);
|
||||||
|
|
||||||
|
// Creates and returns a box gradient. Box gradient is a feathered rounded rectangle, it is useful for rendering
|
||||||
|
// drop shadows or highlights for boxes. Parameters (x,y) define the top-left corner of the rectangle,
|
||||||
|
// (w,h) define the size of the rectangle, r defines the corner radius, and f feather. Feather defines how blurry
|
||||||
|
// the border of the rectangle is. Parameter icol specifies the inner color and ocol the outer color of the gradient.
|
||||||
|
// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint().
|
||||||
|
NVGpaint nvgBoxGradient(NVGcontext* ctx, float x, float y, float w, float h,
|
||||||
|
float r, float f, NVGcolor icol, NVGcolor ocol);
|
||||||
|
|
||||||
|
// Creates and returns a radial gradient. Parameters (cx,cy) specify the center, inr and outr specify
|
||||||
|
// the inner and outer radius of the gradient, icol specifies the start color and ocol the end color.
|
||||||
|
// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint().
|
||||||
|
NVGpaint nvgRadialGradient(NVGcontext* ctx, float cx, float cy, float inr, float outr,
|
||||||
|
NVGcolor icol, NVGcolor ocol);
|
||||||
|
|
||||||
|
// Creates and returns an image patter. Parameters (ox,oy) specify the left-top location of the image pattern,
|
||||||
|
// (ex,ey) the size of one image, angle rotation around the top-left corner, image is handle to the image to render.
|
||||||
|
// The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint().
|
||||||
|
NVGpaint nvgImagePattern(NVGcontext* ctx, float ox, float oy, float ex, float ey,
|
||||||
|
float angle, int image, float alpha);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Scissoring
|
||||||
|
//
|
||||||
|
// Scissoring allows you to clip the rendering into a rectangle. This is useful for various
|
||||||
|
// user interface cases like rendering a text edit or a timeline.
|
||||||
|
|
||||||
|
// Sets the current scissor rectangle.
|
||||||
|
// The scissor rectangle is transformed by the current transform.
|
||||||
|
void nvgScissor(NVGcontext* ctx, float x, float y, float w, float h);
|
||||||
|
|
||||||
|
// Intersects current scissor rectangle with the specified rectangle.
|
||||||
|
// The scissor rectangle is transformed by the current transform.
|
||||||
|
// Note: in case the rotation of previous scissor rect differs from
|
||||||
|
// the current one, the intersection will be done between the specified
|
||||||
|
// rectangle and the previous scissor rectangle transformed in the current
|
||||||
|
// transform space. The resulting shape is always rectangle.
|
||||||
|
void nvgIntersectScissor(NVGcontext* ctx, float x, float y, float w, float h);
|
||||||
|
|
||||||
|
// Reset and disables scissoring.
|
||||||
|
void nvgResetScissor(NVGcontext* ctx);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Paths
|
||||||
|
//
|
||||||
|
// Drawing a new shape starts with nvgBeginPath(), it clears all the currently defined paths.
|
||||||
|
// Then you define one or more paths and sub-paths which describe the shape. The are functions
|
||||||
|
// to draw common shapes like rectangles and circles, and lower level step-by-step functions,
|
||||||
|
// which allow to define a path curve by curve.
|
||||||
|
//
|
||||||
|
// NanoVG uses even-odd fill rule to draw the shapes. Solid shapes should have counter clockwise
|
||||||
|
// winding and holes should have counter clockwise order. To specify winding of a path you can
|
||||||
|
// call nvgPathWinding(). This is useful especially for the common shapes, which are drawn CCW.
|
||||||
|
//
|
||||||
|
// Finally you can fill the path using current fill style by calling nvgFill(), and stroke it
|
||||||
|
// with current stroke style by calling nvgStroke().
|
||||||
|
//
|
||||||
|
// The curve segments and sub-paths are transformed by the current transform.
|
||||||
|
|
||||||
|
// Clears the current path and sub-paths.
|
||||||
|
void nvgBeginPath(NVGcontext* ctx);
|
||||||
|
|
||||||
|
// Starts new sub-path with specified point as first point.
|
||||||
|
void nvgMoveTo(NVGcontext* ctx, float x, float y);
|
||||||
|
|
||||||
|
// Adds line segment from the last point in the path to the specified point.
|
||||||
|
void nvgLineTo(NVGcontext* ctx, float x, float y);
|
||||||
|
|
||||||
|
// Adds cubic bezier segment from last point in the path via two control points to the specified point.
|
||||||
|
void nvgBezierTo(NVGcontext* ctx, float c1x, float c1y, float c2x, float c2y, float x, float y);
|
||||||
|
|
||||||
|
// Adds quadratic bezier segment from last point in the path via a control point to the specified point.
|
||||||
|
void nvgQuadTo(NVGcontext* ctx, float cx, float cy, float x, float y);
|
||||||
|
|
||||||
|
// Adds an arc segment at the corner defined by the last path point, and two specified points.
|
||||||
|
void nvgArcTo(NVGcontext* ctx, float x1, float y1, float x2, float y2, float radius);
|
||||||
|
|
||||||
|
// Closes current sub-path with a line segment.
|
||||||
|
void nvgClosePath(NVGcontext* ctx);
|
||||||
|
|
||||||
|
// Sets the current sub-path winding, see NVGwinding and NVGsolidity.
|
||||||
|
void nvgPathWinding(NVGcontext* ctx, int dir);
|
||||||
|
|
||||||
|
// Creates new circle arc shaped sub-path. The arc center is at cx,cy, the arc radius is r,
|
||||||
|
// and the arc is drawn from angle a0 to a1, and swept in direction dir (NVG_CCW, or NVG_CW).
|
||||||
|
// Angles are specified in radians.
|
||||||
|
void nvgArc(NVGcontext* ctx, float cx, float cy, float r, float a0, float a1, int dir);
|
||||||
|
|
||||||
|
// Creates new rectangle shaped sub-path.
|
||||||
|
void nvgRect(NVGcontext* ctx, float x, float y, float w, float h);
|
||||||
|
|
||||||
|
// Creates new rounded rectangle shaped sub-path.
|
||||||
|
void nvgRoundedRect(NVGcontext* ctx, float x, float y, float w, float h, float r);
|
||||||
|
|
||||||
|
// Creates new rounded rectangle shaped sub-path with varying radii for each corner.
|
||||||
|
void nvgRoundedRectVarying(NVGcontext* ctx, float x, float y, float w, float h, float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft);
|
||||||
|
|
||||||
|
// Creates new ellipse shaped sub-path.
|
||||||
|
void nvgEllipse(NVGcontext* ctx, float cx, float cy, float rx, float ry);
|
||||||
|
|
||||||
|
// Creates new circle shaped sub-path.
|
||||||
|
void nvgCircle(NVGcontext* ctx, float cx, float cy, float r);
|
||||||
|
|
||||||
|
// Fills the current path with current fill style.
|
||||||
|
void nvgFill(NVGcontext* ctx);
|
||||||
|
|
||||||
|
// Fills the current path with current stroke style.
|
||||||
|
void nvgStroke(NVGcontext* ctx);
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Text
|
||||||
|
//
|
||||||
|
// NanoVG allows you to load .ttf files and use the font to render text.
|
||||||
|
//
|
||||||
|
// The appearance of the text can be defined by setting the current text style
|
||||||
|
// and by specifying the fill color. Common text and font settings such as
|
||||||
|
// font size, letter spacing and text align are supported. Font blur allows you
|
||||||
|
// to create simple text effects such as drop shadows.
|
||||||
|
//
|
||||||
|
// At render time the font face can be set based on the font handles or name.
|
||||||
|
//
|
||||||
|
// Font measure functions return values in local space, the calculations are
|
||||||
|
// carried in the same resolution as the final rendering. This is done because
|
||||||
|
// the text glyph positions are snapped to the nearest pixels sharp rendering.
|
||||||
|
//
|
||||||
|
// The local space means that values are not rotated or scale as per the current
|
||||||
|
// transformation. For example if you set font size to 12, which would mean that
|
||||||
|
// line height is 16, then regardless of the current scaling and rotation, the
|
||||||
|
// returned line height is always 16. Some measures may vary because of the scaling
|
||||||
|
// since aforementioned pixel snapping.
|
||||||
|
//
|
||||||
|
// While this may sound a little odd, the setup allows you to always render the
|
||||||
|
// same way regardless of scaling. I.e. following works regardless of scaling:
|
||||||
|
//
|
||||||
|
// const char* txt = "Text me up.";
|
||||||
|
// nvgTextBounds(vg, x,y, txt, NULL, bounds);
|
||||||
|
// nvgBeginPath(vg);
|
||||||
|
// nvgRoundedRect(vg, bounds[0],bounds[1], bounds[2]-bounds[0], bounds[3]-bounds[1]);
|
||||||
|
// nvgFill(vg);
|
||||||
|
//
|
||||||
|
// Note: currently only solid color fill is supported for text.
|
||||||
|
|
||||||
|
// Creates font by loading it from the disk from specified file name.
|
||||||
|
// Returns handle to the font.
|
||||||
|
int nvgCreateFont(NVGcontext* ctx, const char* name, const char* filename);
|
||||||
|
|
||||||
|
// fontIndex specifies which font face to load from a .ttf/.ttc file.
|
||||||
|
int nvgCreateFontAtIndex(NVGcontext* ctx, const char* name, const char* filename, const int fontIndex);
|
||||||
|
|
||||||
|
// Creates font by loading it from the specified memory chunk.
|
||||||
|
// Returns handle to the font.
|
||||||
|
int nvgCreateFontMem(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData);
|
||||||
|
|
||||||
|
// fontIndex specifies which font face to load from a .ttf/.ttc file.
|
||||||
|
int nvgCreateFontMemAtIndex(NVGcontext* ctx, const char* name, unsigned char* data, int ndata, int freeData, const int fontIndex);
|
||||||
|
|
||||||
|
// Finds a loaded font of specified name, and returns handle to it, or -1 if the font is not found.
|
||||||
|
int nvgFindFont(NVGcontext* ctx, const char* name);
|
||||||
|
|
||||||
|
// Adds a fallback font by handle.
|
||||||
|
int nvgAddFallbackFontId(NVGcontext* ctx, int baseFont, int fallbackFont);
|
||||||
|
|
||||||
|
// Adds a fallback font by name.
|
||||||
|
int nvgAddFallbackFont(NVGcontext* ctx, const char* baseFont, const char* fallbackFont);
|
||||||
|
|
||||||
|
// Resets fallback fonts by handle.
|
||||||
|
void nvgResetFallbackFontsId(NVGcontext* ctx, int baseFont);
|
||||||
|
|
||||||
|
// Resets fallback fonts by name.
|
||||||
|
void nvgResetFallbackFonts(NVGcontext* ctx, const char* baseFont);
|
||||||
|
|
||||||
|
// Sets the font size of current text style.
|
||||||
|
void nvgFontSize(NVGcontext* ctx, float size);
|
||||||
|
|
||||||
|
// Sets the blur of current text style.
|
||||||
|
void nvgFontBlur(NVGcontext* ctx, float blur);
|
||||||
|
|
||||||
|
// Sets the letter spacing of current text style.
|
||||||
|
void nvgTextLetterSpacing(NVGcontext* ctx, float spacing);
|
||||||
|
|
||||||
|
// Sets the proportional line height of current text style. The line height is specified as multiple of font size.
|
||||||
|
void nvgTextLineHeight(NVGcontext* ctx, float lineHeight);
|
||||||
|
|
||||||
|
// Sets the text align of current text style, see NVGalign for options.
|
||||||
|
void nvgTextAlign(NVGcontext* ctx, int align);
|
||||||
|
|
||||||
|
// Sets the font face based on specified id of current text style.
|
||||||
|
void nvgFontFaceId(NVGcontext* ctx, int font);
|
||||||
|
|
||||||
|
// Sets the font face based on specified name of current text style.
|
||||||
|
void nvgFontFace(NVGcontext* ctx, const char* font);
|
||||||
|
|
||||||
|
// Draws text string at specified location. If end is specified only the sub-string up to the end is drawn.
|
||||||
|
float nvgText(NVGcontext* ctx, float x, float y, const char* string, const char* end);
|
||||||
|
|
||||||
|
// Draws multi-line text string at specified location wrapped at the specified width. If end is specified only the sub-string up to the end is drawn.
|
||||||
|
// White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered.
|
||||||
|
// Words longer than the max width are slit at nearest character (i.e. no hyphenation).
|
||||||
|
void nvgTextBox(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end);
|
||||||
|
|
||||||
|
// Measures the specified text string. Parameter bounds should be a pointer to float[4],
|
||||||
|
// if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax]
|
||||||
|
// Returns the horizontal advance of the measured text (i.e. where the next character should drawn).
|
||||||
|
// Measured values are returned in local coordinate space.
|
||||||
|
float nvgTextBounds(NVGcontext* ctx, float x, float y, const char* string, const char* end, float* bounds);
|
||||||
|
|
||||||
|
// Measures the specified multi-text string. Parameter bounds should be a pointer to float[4],
|
||||||
|
// if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax]
|
||||||
|
// Measured values are returned in local coordinate space.
|
||||||
|
void nvgTextBoxBounds(NVGcontext* ctx, float x, float y, float breakRowWidth, const char* string, const char* end, float* bounds);
|
||||||
|
|
||||||
|
// Calculates the glyph x positions of the specified text. If end is specified only the sub-string will be used.
|
||||||
|
// Measured values are returned in local coordinate space.
|
||||||
|
int nvgTextGlyphPositions(NVGcontext* ctx, float x, float y, const char* string, const char* end, NVGglyphPosition* positions, int maxPositions);
|
||||||
|
|
||||||
|
// Returns the vertical metrics based on the current text style.
|
||||||
|
// Measured values are returned in local coordinate space.
|
||||||
|
void nvgTextMetrics(NVGcontext* ctx, float* ascender, float* descender, float* lineh);
|
||||||
|
|
||||||
|
// Breaks the specified text into lines. If end is specified only the sub-string will be used.
|
||||||
|
// White space is stripped at the beginning of the rows, the text is split at word boundaries or when new-line characters are encountered.
|
||||||
|
// Words longer than the max width are slit at nearest character (i.e. no hyphenation).
|
||||||
|
int nvgTextBreakLines(NVGcontext* ctx, const char* string, const char* end, float breakRowWidth, NVGtextRow* rows, int maxRows);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Internal Render API
|
||||||
|
//
|
||||||
|
enum NVGtexture {
|
||||||
|
NVG_TEXTURE_ALPHA = 0x01,
|
||||||
|
NVG_TEXTURE_RGBA = 0x02,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NVGscissor {
|
||||||
|
float xform[6];
|
||||||
|
float extent[2];
|
||||||
|
};
|
||||||
|
typedef struct NVGscissor NVGscissor;
|
||||||
|
|
||||||
|
struct NVGvertex {
|
||||||
|
float x,y,u,v;
|
||||||
|
};
|
||||||
|
typedef struct NVGvertex NVGvertex;
|
||||||
|
|
||||||
|
struct NVGpath {
|
||||||
|
int first;
|
||||||
|
int count;
|
||||||
|
unsigned char closed;
|
||||||
|
int nbevel;
|
||||||
|
NVGvertex* fill;
|
||||||
|
int nfill;
|
||||||
|
NVGvertex* stroke;
|
||||||
|
int nstroke;
|
||||||
|
int winding;
|
||||||
|
int convex;
|
||||||
|
};
|
||||||
|
typedef struct NVGpath NVGpath;
|
||||||
|
|
||||||
|
struct NVGparams {
|
||||||
|
void* userPtr;
|
||||||
|
int edgeAntiAlias;
|
||||||
|
int (*renderCreate)(void* uptr);
|
||||||
|
int (*renderCreateTexture)(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data);
|
||||||
|
int (*renderDeleteTexture)(void* uptr, int image);
|
||||||
|
int (*renderUpdateTexture)(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data);
|
||||||
|
int (*renderGetTextureSize)(void* uptr, int image, int* w, int* h);
|
||||||
|
void (*renderViewport)(void* uptr, float width, float height, float devicePixelRatio);
|
||||||
|
void (*renderCancel)(void* uptr);
|
||||||
|
void (*renderFlush)(void* uptr);
|
||||||
|
void (*renderFill)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, const float* bounds, const NVGpath* paths, int npaths);
|
||||||
|
void (*renderStroke)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe, float strokeWidth, const NVGpath* paths, int npaths);
|
||||||
|
void (*renderTriangles)(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, const NVGvertex* verts, int nverts, float fringe);
|
||||||
|
void (*renderDelete)(void* uptr);
|
||||||
|
};
|
||||||
|
typedef struct NVGparams NVGparams;
|
||||||
|
|
||||||
|
// Constructor and destructor, called by the render back-end.
|
||||||
|
NVGcontext* nvgCreateInternal(NVGparams* params);
|
||||||
|
void nvgDeleteInternal(NVGcontext* ctx);
|
||||||
|
|
||||||
|
NVGparams* nvgInternalParams(NVGcontext* ctx);
|
||||||
|
|
||||||
|
// Debug function to dump cached path data.
|
||||||
|
void nvgDebugDumpPathCache(NVGcontext* ctx);
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NVG_NOTUSED(v) for (;;) { (void)(1 ? (void)0 : ( (void)(v) ) ); break; }
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // NANOVG_H
|
207
troposphere/daybreak/nanovg/include/nanovg/dk_renderer.hpp
Normal file
207
troposphere/daybreak/nanovg/include/nanovg/dk_renderer.hpp
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <deko3d.hpp>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "framework/CDescriptorSet.h"
|
||||||
|
#include "framework/CMemPool.h"
|
||||||
|
#include "framework/CShader.h"
|
||||||
|
#include "framework/CCmdMemRing.h"
|
||||||
|
#include "nanovg.h"
|
||||||
|
|
||||||
|
// Create flags
|
||||||
|
enum NVGcreateFlags {
|
||||||
|
// Flag indicating if geometry based anti-aliasing is used (may not be needed when using MSAA).
|
||||||
|
NVG_ANTIALIAS = 1<<0,
|
||||||
|
// Flag indicating if strokes should be drawn using stencil buffer. The rendering will be a little
|
||||||
|
// slower, but path overlaps (i.e. self-intersecting or sharp turns) will be drawn just once.
|
||||||
|
NVG_STENCIL_STROKES = 1<<1,
|
||||||
|
// Flag indicating that additional debug checks are done.
|
||||||
|
NVG_DEBUG = 1<<2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum DKNVGuniformLoc
|
||||||
|
{
|
||||||
|
DKNVG_LOC_VIEWSIZE,
|
||||||
|
DKNVG_LOC_TEX,
|
||||||
|
DKNVG_LOC_FRAG,
|
||||||
|
DKNVG_MAX_LOCS
|
||||||
|
};
|
||||||
|
|
||||||
|
enum VKNVGshaderType {
|
||||||
|
NSVG_SHADER_FILLGRAD,
|
||||||
|
NSVG_SHADER_FILLIMG,
|
||||||
|
NSVG_SHADER_SIMPLE,
|
||||||
|
NSVG_SHADER_IMG
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DKNVGtextureDescriptor {
|
||||||
|
int width, height;
|
||||||
|
int type;
|
||||||
|
int flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DKNVGblend {
|
||||||
|
int srcRGB;
|
||||||
|
int dstRGB;
|
||||||
|
int srcAlpha;
|
||||||
|
int dstAlpha;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum DKNVGcallType {
|
||||||
|
DKNVG_NONE = 0,
|
||||||
|
DKNVG_FILL,
|
||||||
|
DKNVG_CONVEXFILL,
|
||||||
|
DKNVG_STROKE,
|
||||||
|
DKNVG_TRIANGLES,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DKNVGcall {
|
||||||
|
int type;
|
||||||
|
int image;
|
||||||
|
int pathOffset;
|
||||||
|
int pathCount;
|
||||||
|
int triangleOffset;
|
||||||
|
int triangleCount;
|
||||||
|
int uniformOffset;
|
||||||
|
DKNVGblend blendFunc;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DKNVGpath {
|
||||||
|
int fillOffset;
|
||||||
|
int fillCount;
|
||||||
|
int strokeOffset;
|
||||||
|
int strokeCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DKNVGfragUniforms {
|
||||||
|
float scissorMat[12]; // matrices are actually 3 vec4s
|
||||||
|
float paintMat[12];
|
||||||
|
struct NVGcolor innerCol;
|
||||||
|
struct NVGcolor outerCol;
|
||||||
|
float scissorExt[2];
|
||||||
|
float scissorScale[2];
|
||||||
|
float extent[2];
|
||||||
|
float radius;
|
||||||
|
float feather;
|
||||||
|
float strokeMult;
|
||||||
|
float strokeThr;
|
||||||
|
int texType;
|
||||||
|
int type;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace nvg {
|
||||||
|
class DkRenderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DKNVGcontext {
|
||||||
|
nvg::DkRenderer *renderer;
|
||||||
|
float view[2];
|
||||||
|
int fragSize;
|
||||||
|
int flags;
|
||||||
|
// Per frame buffers
|
||||||
|
DKNVGcall* calls;
|
||||||
|
int ccalls;
|
||||||
|
int ncalls;
|
||||||
|
DKNVGpath* paths;
|
||||||
|
int cpaths;
|
||||||
|
int npaths;
|
||||||
|
struct NVGvertex* verts;
|
||||||
|
int cverts;
|
||||||
|
int nverts;
|
||||||
|
unsigned char* uniforms;
|
||||||
|
int cuniforms;
|
||||||
|
int nuniforms;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace nvg {
|
||||||
|
|
||||||
|
class Texture {
|
||||||
|
private:
|
||||||
|
const int m_id;
|
||||||
|
dk::Image m_image;
|
||||||
|
dk::ImageDescriptor m_image_descriptor;
|
||||||
|
CMemPool::Handle m_image_mem;
|
||||||
|
DKNVGtextureDescriptor m_texture_descriptor;
|
||||||
|
public:
|
||||||
|
Texture(int id);
|
||||||
|
~Texture();
|
||||||
|
|
||||||
|
void Initialize(CMemPool &image_pool, CMemPool &scratch_pool, dk::Device device, dk::Queue transfer_queue, int type, int w, int h, int image_flags, const u8 *data);
|
||||||
|
void Update(CMemPool &image_pool, CMemPool &scratch_pool, dk::Device device, dk::Queue transfer_queue, int type, int w, int h, int image_flags, const u8 *data);
|
||||||
|
|
||||||
|
int GetId();
|
||||||
|
const DKNVGtextureDescriptor &GetDescriptor();
|
||||||
|
|
||||||
|
dk::Image &GetImage();
|
||||||
|
dk::ImageDescriptor &GetImageDescriptor();
|
||||||
|
};
|
||||||
|
|
||||||
|
class DkRenderer {
|
||||||
|
private:
|
||||||
|
enum SamplerType : u8 {
|
||||||
|
SamplerType_MipFilter = 1 << 0,
|
||||||
|
SamplerType_Nearest = 1 << 1,
|
||||||
|
SamplerType_RepeatX = 1 << 2,
|
||||||
|
SamplerType_RepeatY = 1 << 3,
|
||||||
|
SamplerType_Total = 0x10,
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
static constexpr size_t DynamicCmdSize = 0x20000;
|
||||||
|
static constexpr size_t FragmentUniformSize = sizeof(DKNVGfragUniforms) + 4 - sizeof(DKNVGfragUniforms) % 4;
|
||||||
|
static constexpr size_t MaxImages = 0x1000;
|
||||||
|
|
||||||
|
/* From the application. */
|
||||||
|
u32 m_view_width;
|
||||||
|
u32 m_view_height;
|
||||||
|
dk::Device m_device;
|
||||||
|
dk::Queue m_queue;
|
||||||
|
CMemPool &m_image_mem_pool;
|
||||||
|
CMemPool &m_code_mem_pool;
|
||||||
|
CMemPool &m_data_mem_pool;
|
||||||
|
|
||||||
|
/* State. */
|
||||||
|
dk::UniqueCmdBuf m_dyn_cmd_buf;
|
||||||
|
CCmdMemRing<1> m_dyn_cmd_mem;
|
||||||
|
std::optional<CMemPool::Handle> m_vertex_buffer;
|
||||||
|
CShader m_vertex_shader;
|
||||||
|
CShader m_fragment_shader;
|
||||||
|
CMemPool::Handle m_view_uniform_buffer;
|
||||||
|
CMemPool::Handle m_frag_uniform_buffer;
|
||||||
|
|
||||||
|
u32 m_next_texture_id = 1;
|
||||||
|
std::vector<std::shared_ptr<Texture>> m_textures;
|
||||||
|
CDescriptorSet<MaxImages> m_image_descriptor_set;
|
||||||
|
CDescriptorSet<SamplerType_Total> m_sampler_descriptor_set;
|
||||||
|
std::array<int, MaxImages> m_image_descriptor_mappings;
|
||||||
|
int m_last_image_descriptor = 0;
|
||||||
|
|
||||||
|
int AcquireImageDescriptor(std::shared_ptr<Texture> texture, int image);
|
||||||
|
void FreeImageDescriptor(int image);
|
||||||
|
void SetUniforms(const DKNVGcontext &ctx, int offset, int image);
|
||||||
|
|
||||||
|
void UpdateVertexBuffer(const void *data, size_t size);
|
||||||
|
|
||||||
|
void DrawFill(const DKNVGcontext &ctx, const DKNVGcall &call);
|
||||||
|
void DrawConvexFill(const DKNVGcontext &ctx, const DKNVGcall &call);
|
||||||
|
void DrawStroke(const DKNVGcontext &ctx, const DKNVGcall &call);
|
||||||
|
void DrawTriangles(const DKNVGcontext &ctx, const DKNVGcall &call);
|
||||||
|
|
||||||
|
std::shared_ptr<Texture> FindTexture(int id);
|
||||||
|
public:
|
||||||
|
DkRenderer(unsigned int view_width, unsigned int view_height, dk::Device device, dk::Queue queue, CMemPool &image_mem_pool, CMemPool &code_mem_pool, CMemPool &data_mem_pool);
|
||||||
|
~DkRenderer();
|
||||||
|
|
||||||
|
int Create(DKNVGcontext &ctx);
|
||||||
|
int CreateTexture(const DKNVGcontext &ctx, int type, int w, int h, int image_flags, const u8 *data);
|
||||||
|
int DeleteTexture(const DKNVGcontext &ctx, int id);
|
||||||
|
int UpdateTexture(const DKNVGcontext &ctx, int id, int x, int y, int w, int h, const u8 *data);
|
||||||
|
int GetTextureSize(const DKNVGcontext &ctx, int id, int *w, int *h);
|
||||||
|
const DKNVGtextureDescriptor *GetTextureDescriptor(const DKNVGcontext &ctx, int id);
|
||||||
|
|
||||||
|
void Flush(DKNVGcontext &ctx);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
1785
troposphere/daybreak/nanovg/include/nanovg/fontstash.h
Normal file
1785
troposphere/daybreak/nanovg/include/nanovg/fontstash.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CApplication.h: Wrapper class containing common application boilerplate
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
class CApplication
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
virtual void onFocusState(AppletFocusState) { }
|
||||||
|
virtual void onOperationMode(AppletOperationMode) { }
|
||||||
|
virtual bool onFrame(u64) { return true; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
CApplication();
|
||||||
|
~CApplication();
|
||||||
|
|
||||||
|
void run();
|
||||||
|
|
||||||
|
static constexpr void chooseFramebufferSize(uint32_t& width, uint32_t& height, AppletOperationMode mode);
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr void CApplication::chooseFramebufferSize(uint32_t& width, uint32_t& height, AppletOperationMode mode)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case AppletOperationMode_Handheld:
|
||||||
|
width = 1280;
|
||||||
|
height = 720;
|
||||||
|
break;
|
||||||
|
case AppletOperationMode_Docked:
|
||||||
|
width = 1920;
|
||||||
|
height = 1080;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CCmdMemRing.h: Memory provider class for dynamic command buffers
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "common.h"
|
||||||
|
#include "CMemPool.h"
|
||||||
|
|
||||||
|
template <unsigned NumSlices>
|
||||||
|
class CCmdMemRing
|
||||||
|
{
|
||||||
|
static_assert(NumSlices > 0, "Need a non-zero number of slices...");
|
||||||
|
CMemPool::Handle m_mem;
|
||||||
|
unsigned m_curSlice;
|
||||||
|
dk::Fence m_fences[NumSlices];
|
||||||
|
public:
|
||||||
|
CCmdMemRing() : m_mem{}, m_curSlice{}, m_fences{} { }
|
||||||
|
~CCmdMemRing()
|
||||||
|
{
|
||||||
|
m_mem.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool allocate(CMemPool& pool, uint32_t sliceSize)
|
||||||
|
{
|
||||||
|
sliceSize = (sliceSize + DK_CMDMEM_ALIGNMENT - 1) &~ (DK_CMDMEM_ALIGNMENT - 1);
|
||||||
|
m_mem = pool.allocate(NumSlices*sliceSize);
|
||||||
|
return m_mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
void begin(dk::CmdBuf cmdbuf)
|
||||||
|
{
|
||||||
|
// Clear/reset the command buffer, which also destroys all command list handles
|
||||||
|
// (but remember: it does *not* in fact destroy the command data)
|
||||||
|
cmdbuf.clear();
|
||||||
|
|
||||||
|
// Wait for the current slice of memory to be available, and feed it to the command buffer
|
||||||
|
uint32_t sliceSize = m_mem.getSize() / NumSlices;
|
||||||
|
m_fences[m_curSlice].wait();
|
||||||
|
|
||||||
|
// Feed the memory to the command buffer
|
||||||
|
cmdbuf.addMemory(m_mem.getMemBlock(), m_mem.getOffset() + m_curSlice * sliceSize, sliceSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
DkCmdList end(dk::CmdBuf cmdbuf)
|
||||||
|
{
|
||||||
|
// Signal the fence corresponding to the current slice; so that in the future when we want
|
||||||
|
// to use it again, we can wait for the completion of the commands we've just submitted
|
||||||
|
// (and as such we don't overwrite in-flight command data with new one)
|
||||||
|
cmdbuf.signalFence(m_fences[m_curSlice]);
|
||||||
|
|
||||||
|
// Advance the current slice counter; wrapping around when we reach the end
|
||||||
|
m_curSlice = (m_curSlice + 1) % NumSlices;
|
||||||
|
|
||||||
|
// Finish off the command list, returning it to the caller
|
||||||
|
return cmdbuf.finishList();
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CDescriptorSet.h: Image/Sampler descriptor set class
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "common.h"
|
||||||
|
#include "CMemPool.h"
|
||||||
|
|
||||||
|
template <unsigned NumDescriptors>
|
||||||
|
class CDescriptorSet
|
||||||
|
{
|
||||||
|
static_assert(NumDescriptors > 0, "Need a non-zero number of descriptors...");
|
||||||
|
static_assert(sizeof(DkImageDescriptor) == sizeof(DkSamplerDescriptor), "shouldn't happen");
|
||||||
|
static_assert(DK_IMAGE_DESCRIPTOR_ALIGNMENT == DK_SAMPLER_DESCRIPTOR_ALIGNMENT, "shouldn't happen");
|
||||||
|
static constexpr size_t DescriptorSize = sizeof(DkImageDescriptor);
|
||||||
|
static constexpr size_t DescriptorAlign = DK_IMAGE_DESCRIPTOR_ALIGNMENT;
|
||||||
|
|
||||||
|
CMemPool::Handle m_mem;
|
||||||
|
public:
|
||||||
|
CDescriptorSet() : m_mem{} { }
|
||||||
|
~CDescriptorSet()
|
||||||
|
{
|
||||||
|
m_mem.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool allocate(CMemPool& pool)
|
||||||
|
{
|
||||||
|
m_mem = pool.allocate(NumDescriptors*DescriptorSize, DescriptorAlign);
|
||||||
|
return m_mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bindForImages(dk::CmdBuf cmdbuf)
|
||||||
|
{
|
||||||
|
cmdbuf.bindImageDescriptorSet(m_mem.getGpuAddr(), NumDescriptors);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bindForSamplers(dk::CmdBuf cmdbuf)
|
||||||
|
{
|
||||||
|
cmdbuf.bindSamplerDescriptorSet(m_mem.getGpuAddr(), NumDescriptors);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void update(dk::CmdBuf cmdbuf, uint32_t id, T const& descriptor)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(T) == DescriptorSize);
|
||||||
|
cmdbuf.pushData(m_mem.getGpuAddr() + id*DescriptorSize, &descriptor, DescriptorSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, size_t N>
|
||||||
|
void update(dk::CmdBuf cmdbuf, uint32_t id, std::array<T, N> const& descriptors)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(T) == DescriptorSize);
|
||||||
|
cmdbuf.pushData(m_mem.getGpuAddr() + id*DescriptorSize, descriptors.data(), descriptors.size()*DescriptorSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DK_HPP_SUPPORT_VECTOR
|
||||||
|
template <typename T, typename Allocator = std::allocator<T>>
|
||||||
|
void update(dk::CmdBuf cmdbuf, uint32_t id, std::vector<T,Allocator> const& descriptors)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(T) == DescriptorSize);
|
||||||
|
cmdbuf.pushData(m_mem.getGpuAddr() + id*DescriptorSize, descriptors.data(), descriptors.size()*DescriptorSize);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void update(dk::CmdBuf cmdbuf, uint32_t id, std::initializer_list<T const> const& descriptors)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(T) == DescriptorSize);
|
||||||
|
cmdbuf.pushData(m_mem.getGpuAddr() + id*DescriptorSize, descriptors.data(), descriptors.size()*DescriptorSize);
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CExternalImage.h: Utility class for loading images from the filesystem
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "common.h"
|
||||||
|
#include "CMemPool.h"
|
||||||
|
|
||||||
|
class CExternalImage
|
||||||
|
{
|
||||||
|
dk::Image m_image;
|
||||||
|
dk::ImageDescriptor m_descriptor;
|
||||||
|
CMemPool::Handle m_mem;
|
||||||
|
public:
|
||||||
|
CExternalImage() : m_image{}, m_descriptor{}, m_mem{} { }
|
||||||
|
~CExternalImage()
|
||||||
|
{
|
||||||
|
m_mem.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr operator bool() const
|
||||||
|
{
|
||||||
|
return m_mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr dk::Image& get()
|
||||||
|
{
|
||||||
|
return m_image;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr dk::ImageDescriptor const& getDescriptor() const
|
||||||
|
{
|
||||||
|
return m_descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool load(CMemPool& imagePool, CMemPool& scratchPool, dk::Device device, dk::Queue transferQueue, const char* path, uint32_t width, uint32_t height, DkImageFormat format, uint32_t flags = 0);
|
||||||
|
};
|
|
@ -0,0 +1,119 @@
|
||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CIntrusiveList.h: Intrusive doubly-linked list helper class
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct CIntrusiveListNode
|
||||||
|
{
|
||||||
|
T *m_next, *m_prev;
|
||||||
|
|
||||||
|
constexpr CIntrusiveListNode() : m_next{}, m_prev{} { }
|
||||||
|
constexpr operator bool() const { return m_next || m_prev; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, CIntrusiveListNode<T> T::* node_ptr>
|
||||||
|
class CIntrusiveList
|
||||||
|
{
|
||||||
|
T *m_first, *m_last;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr CIntrusiveList() : m_first{}, m_last{} { }
|
||||||
|
|
||||||
|
constexpr T* first() const { return m_first; }
|
||||||
|
constexpr T* last() const { return m_last; }
|
||||||
|
constexpr bool empty() const { return !m_first; }
|
||||||
|
constexpr void clear() { m_first = m_last = nullptr; }
|
||||||
|
|
||||||
|
constexpr bool isLinked(T* obj) const { return obj->*node_ptr || m_first == obj; }
|
||||||
|
constexpr T* prev(T* obj) const { return (obj->*node_ptr).m_prev; }
|
||||||
|
constexpr T* next(T* obj) const { return (obj->*node_ptr).m_next; }
|
||||||
|
|
||||||
|
void add(T* obj)
|
||||||
|
{
|
||||||
|
return addBefore(nullptr, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addBefore(T* pos, T* obj)
|
||||||
|
{
|
||||||
|
auto& node = obj->*node_ptr;
|
||||||
|
node.m_next = pos;
|
||||||
|
node.m_prev = pos ? (pos->*node_ptr).m_prev : m_last;
|
||||||
|
|
||||||
|
if (pos)
|
||||||
|
(pos->*node_ptr).m_prev = obj;
|
||||||
|
else
|
||||||
|
m_last = obj;
|
||||||
|
|
||||||
|
if (node.m_prev)
|
||||||
|
(node.m_prev->*node_ptr).m_next = obj;
|
||||||
|
else
|
||||||
|
m_first = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addAfter(T* pos, T* obj)
|
||||||
|
{
|
||||||
|
auto& node = obj->*node_ptr;
|
||||||
|
node.m_next = pos ? (pos->*node_ptr).m_next : m_first;
|
||||||
|
node.m_prev = pos;
|
||||||
|
|
||||||
|
if (pos)
|
||||||
|
(pos->*node_ptr).m_next = obj;
|
||||||
|
else
|
||||||
|
m_first = obj;
|
||||||
|
|
||||||
|
if (node.m_next)
|
||||||
|
(node.m_next->*node_ptr).m_prev = obj;
|
||||||
|
else
|
||||||
|
m_last = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
T* pop()
|
||||||
|
{
|
||||||
|
T* ret = m_first;
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
m_first = (ret->*node_ptr).m_next;
|
||||||
|
if (m_first)
|
||||||
|
(m_first->*node_ptr).m_prev = nullptr;
|
||||||
|
else
|
||||||
|
m_last = nullptr;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(T* obj)
|
||||||
|
{
|
||||||
|
auto& node = obj->*node_ptr;
|
||||||
|
if (node.m_prev)
|
||||||
|
{
|
||||||
|
(node.m_prev->*node_ptr).m_next = node.m_next;
|
||||||
|
if (node.m_next)
|
||||||
|
(node.m_next->*node_ptr).m_prev = node.m_prev;
|
||||||
|
else
|
||||||
|
m_last = node.m_prev;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
m_first = node.m_next;
|
||||||
|
if (m_first)
|
||||||
|
(m_first->*node_ptr).m_prev = nullptr;
|
||||||
|
else
|
||||||
|
m_last = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
node.m_next = node.m_prev = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename L>
|
||||||
|
void iterate(L lambda) const
|
||||||
|
{
|
||||||
|
T* next = nullptr;
|
||||||
|
for (T* cur = m_first; cur; cur = next)
|
||||||
|
{
|
||||||
|
next = (cur->*node_ptr).m_next;
|
||||||
|
lambda(cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,250 @@
|
||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CIntrusiveTree.h: Intrusive red-black tree helper class
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
struct CIntrusiveTreeNode
|
||||||
|
{
|
||||||
|
enum Color
|
||||||
|
{
|
||||||
|
Red,
|
||||||
|
Black,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Leaf
|
||||||
|
{
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
uintptr_t m_parent_color;
|
||||||
|
CIntrusiveTreeNode* m_children[2];
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr CIntrusiveTreeNode() : m_parent_color{}, m_children{} { }
|
||||||
|
|
||||||
|
constexpr CIntrusiveTreeNode* getParent() const
|
||||||
|
{
|
||||||
|
return reinterpret_cast<CIntrusiveTreeNode*>(m_parent_color &~ 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setParent(CIntrusiveTreeNode* parent)
|
||||||
|
{
|
||||||
|
m_parent_color = (m_parent_color & 1) | reinterpret_cast<uintptr_t>(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Color getColor() const
|
||||||
|
{
|
||||||
|
return static_cast<Color>(m_parent_color & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setColor(Color color)
|
||||||
|
{
|
||||||
|
m_parent_color = (m_parent_color &~ 1) | static_cast<uintptr_t>(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr CIntrusiveTreeNode*& child(Leaf leaf)
|
||||||
|
{
|
||||||
|
return m_children[leaf];
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr CIntrusiveTreeNode* const& child(Leaf leaf) const
|
||||||
|
{
|
||||||
|
return m_children[leaf];
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------
|
||||||
|
|
||||||
|
constexpr bool isRed() const { return getColor() == Red; }
|
||||||
|
constexpr bool isBlack() const { return getColor() == Black; }
|
||||||
|
void setRed() { setColor(Red); }
|
||||||
|
void setBlack() { setColor(Black); }
|
||||||
|
|
||||||
|
constexpr CIntrusiveTreeNode*& left() { return child(Left); }
|
||||||
|
constexpr CIntrusiveTreeNode*& right() { return child(Right); }
|
||||||
|
constexpr CIntrusiveTreeNode* const& left() const { return child(Left); }
|
||||||
|
constexpr CIntrusiveTreeNode* const& right() const { return child(Right); }
|
||||||
|
};
|
||||||
|
|
||||||
|
NX_CONSTEXPR CIntrusiveTreeNode::Leaf operator!(CIntrusiveTreeNode::Leaf val) noexcept
|
||||||
|
{
|
||||||
|
return static_cast<CIntrusiveTreeNode::Leaf>(!static_cast<unsigned>(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
class CIntrusiveTreeBase
|
||||||
|
{
|
||||||
|
using N = CIntrusiveTreeNode;
|
||||||
|
|
||||||
|
void rotate(N* node, N::Leaf leaf);
|
||||||
|
void recolor(N* parent, N* node);
|
||||||
|
protected:
|
||||||
|
N* m_root;
|
||||||
|
|
||||||
|
constexpr CIntrusiveTreeBase() : m_root{} { }
|
||||||
|
|
||||||
|
N* walk(N* node, N::Leaf leaf) const;
|
||||||
|
void insert(N* node, N* parent);
|
||||||
|
void remove(N* node);
|
||||||
|
|
||||||
|
N* minmax(N::Leaf leaf) const
|
||||||
|
{
|
||||||
|
N* p = m_root;
|
||||||
|
if (!p)
|
||||||
|
return nullptr;
|
||||||
|
while (p->child(leaf))
|
||||||
|
p = p->child(leaf);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename H>
|
||||||
|
N*& navigate(N*& node, N*& parent, N::Leaf leafOnEqual, H helm) const
|
||||||
|
{
|
||||||
|
node = nullptr;
|
||||||
|
parent = nullptr;
|
||||||
|
|
||||||
|
N** point = const_cast<N**>(&m_root);
|
||||||
|
while (*point)
|
||||||
|
{
|
||||||
|
int direction = helm(*point);
|
||||||
|
parent = *point;
|
||||||
|
if (direction < 0)
|
||||||
|
point = &(*point)->left();
|
||||||
|
else if (direction > 0)
|
||||||
|
point = &(*point)->right();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
node = *point;
|
||||||
|
point = &(*point)->child(leafOnEqual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *point;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ClassT, typename MemberT>
|
||||||
|
constexpr ClassT* parent_obj(MemberT* member, MemberT ClassT::* ptr)
|
||||||
|
{
|
||||||
|
union whatever
|
||||||
|
{
|
||||||
|
MemberT ClassT::* ptr;
|
||||||
|
intptr_t offset;
|
||||||
|
};
|
||||||
|
// This is technically UB, but basically every compiler worth using admits it as an extension
|
||||||
|
return (ClassT*)((intptr_t)member - whatever{ptr}.offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename T,
|
||||||
|
CIntrusiveTreeNode T::* node_ptr,
|
||||||
|
typename Comparator = std::less<>
|
||||||
|
>
|
||||||
|
class CIntrusiveTree final : protected CIntrusiveTreeBase
|
||||||
|
{
|
||||||
|
using N = CIntrusiveTreeNode;
|
||||||
|
|
||||||
|
static constexpr T* toType(N* m)
|
||||||
|
{
|
||||||
|
return m ? parent_obj(m, node_ptr) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr N* toNode(T* m)
|
||||||
|
{
|
||||||
|
return m ? &(m->*node_ptr) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
static int compare(A const& a, B const& b)
|
||||||
|
{
|
||||||
|
Comparator comp;
|
||||||
|
if (comp(a, b))
|
||||||
|
return -1;
|
||||||
|
if (comp(b, a))
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr CIntrusiveTree() : CIntrusiveTreeBase{} { }
|
||||||
|
|
||||||
|
T* first() const { return toType(minmax(N::Left)); }
|
||||||
|
T* last() const { return toType(minmax(N::Right)); }
|
||||||
|
bool empty() const { return m_root != nullptr; }
|
||||||
|
void clear() { m_root = nullptr; }
|
||||||
|
|
||||||
|
T* prev(T* node) const { return toType(walk(toNode(node), N::Left)); }
|
||||||
|
T* next(T* node) const { return toType(walk(toNode(node), N::Right)); }
|
||||||
|
|
||||||
|
enum SearchMode
|
||||||
|
{
|
||||||
|
Exact = 0,
|
||||||
|
LowerBound = 1,
|
||||||
|
UpperBound = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Lambda>
|
||||||
|
T* search(SearchMode mode, Lambda lambda) const
|
||||||
|
{
|
||||||
|
N *node, *parent;
|
||||||
|
N*& point = navigate(node, parent,
|
||||||
|
mode != UpperBound ? N::Left : N::Right,
|
||||||
|
[&lambda](N* curnode) { return lambda(toType(curnode)); });
|
||||||
|
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case Exact:
|
||||||
|
break;
|
||||||
|
case LowerBound:
|
||||||
|
if (!node && parent)
|
||||||
|
{
|
||||||
|
if (&parent->left() == &point)
|
||||||
|
node = parent;
|
||||||
|
else
|
||||||
|
node = walk(parent, N::Right);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case UpperBound:
|
||||||
|
if (node)
|
||||||
|
node = walk(node, N::Right);
|
||||||
|
else if (parent)
|
||||||
|
{
|
||||||
|
if (&parent->right() == &point)
|
||||||
|
node = walk(parent, N::Right);
|
||||||
|
else
|
||||||
|
node = parent;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return toType(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename K>
|
||||||
|
T* find(K const& key, SearchMode mode = Exact) const
|
||||||
|
{
|
||||||
|
return search(mode, [&key](T* obj) { return compare(key, *obj); });
|
||||||
|
}
|
||||||
|
|
||||||
|
T* insert(T* obj, bool allow_dupes = false)
|
||||||
|
{
|
||||||
|
N *node, *parent;
|
||||||
|
N*& point = navigate(node, parent, N::Right,
|
||||||
|
[obj](N* curnode) { return compare(*obj, *toType(curnode)); });
|
||||||
|
|
||||||
|
if (node && !allow_dupes)
|
||||||
|
return toType(node);
|
||||||
|
|
||||||
|
point = toNode(obj);
|
||||||
|
CIntrusiveTreeBase::insert(point, parent);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(T* obj)
|
||||||
|
{
|
||||||
|
CIntrusiveTreeBase::remove(toNode(obj));
|
||||||
|
}
|
||||||
|
};
|
120
troposphere/daybreak/nanovg/include/nanovg/framework/CMemPool.h
Normal file
120
troposphere/daybreak/nanovg/include/nanovg/framework/CMemPool.h
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CMemPool.h: Pooled dynamic memory allocation manager class
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "common.h"
|
||||||
|
#include "CIntrusiveList.h"
|
||||||
|
#include "CIntrusiveTree.h"
|
||||||
|
|
||||||
|
class CMemPool
|
||||||
|
{
|
||||||
|
dk::Device m_dev;
|
||||||
|
uint32_t m_flags;
|
||||||
|
uint32_t m_blockSize;
|
||||||
|
|
||||||
|
struct Block
|
||||||
|
{
|
||||||
|
CIntrusiveListNode<Block> m_node;
|
||||||
|
dk::MemBlock m_obj;
|
||||||
|
void* m_cpuAddr;
|
||||||
|
DkGpuAddr m_gpuAddr;
|
||||||
|
|
||||||
|
constexpr void* cpuOffset(uint32_t offset) const
|
||||||
|
{
|
||||||
|
return m_cpuAddr ? ((u8*)m_cpuAddr + offset) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr DkGpuAddr gpuOffset(uint32_t offset) const
|
||||||
|
{
|
||||||
|
return m_gpuAddr != DK_GPU_ADDR_INVALID ? (m_gpuAddr + offset) : DK_GPU_ADDR_INVALID;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CIntrusiveList<Block, &Block::m_node> m_blocks;
|
||||||
|
|
||||||
|
struct Slice
|
||||||
|
{
|
||||||
|
CIntrusiveListNode<Slice> m_node;
|
||||||
|
CIntrusiveTreeNode m_treenode;
|
||||||
|
CMemPool* m_pool;
|
||||||
|
Block* m_block;
|
||||||
|
uint32_t m_start;
|
||||||
|
uint32_t m_end;
|
||||||
|
|
||||||
|
constexpr uint32_t getSize() const { return m_end - m_start; }
|
||||||
|
constexpr bool canCoalesce(Slice const& rhs) const { return m_pool == rhs.m_pool && m_block == rhs.m_block && m_end == rhs.m_start; }
|
||||||
|
|
||||||
|
constexpr bool operator<(Slice const& rhs) const { return getSize() < rhs.getSize(); }
|
||||||
|
constexpr bool operator<(uint32_t rhs) const { return getSize() < rhs; }
|
||||||
|
};
|
||||||
|
|
||||||
|
friend constexpr bool operator<(uint32_t lhs, Slice const& rhs);
|
||||||
|
|
||||||
|
CIntrusiveList<Slice, &Slice::m_node> m_memMap, m_sliceHeap;
|
||||||
|
CIntrusiveTree<Slice, &Slice::m_treenode> m_freeList;
|
||||||
|
|
||||||
|
Slice* _newSlice();
|
||||||
|
void _deleteSlice(Slice*);
|
||||||
|
|
||||||
|
void _destroy(Slice* slice);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr uint32_t DefaultBlockSize = 0x800000;
|
||||||
|
class Handle
|
||||||
|
{
|
||||||
|
Slice* m_slice;
|
||||||
|
public:
|
||||||
|
constexpr Handle(Slice* slice = nullptr) : m_slice{slice} { }
|
||||||
|
constexpr operator bool() const { return m_slice != nullptr; }
|
||||||
|
constexpr operator Slice*() const { return m_slice; }
|
||||||
|
constexpr bool operator!() const { return !m_slice; }
|
||||||
|
constexpr bool operator==(Handle const& rhs) const { return m_slice == rhs.m_slice; }
|
||||||
|
constexpr bool operator!=(Handle const& rhs) const { return m_slice != rhs.m_slice; }
|
||||||
|
|
||||||
|
void destroy()
|
||||||
|
{
|
||||||
|
if (m_slice)
|
||||||
|
{
|
||||||
|
m_slice->m_pool->_destroy(m_slice);
|
||||||
|
m_slice = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr dk::MemBlock getMemBlock() const
|
||||||
|
{
|
||||||
|
return m_slice->m_block->m_obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr uint32_t getOffset() const
|
||||||
|
{
|
||||||
|
return m_slice->m_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr uint32_t getSize() const
|
||||||
|
{
|
||||||
|
return m_slice->getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void* getCpuAddr() const
|
||||||
|
{
|
||||||
|
return m_slice->m_block->cpuOffset(m_slice->m_start);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr DkGpuAddr getGpuAddr() const
|
||||||
|
{
|
||||||
|
return m_slice->m_block->gpuOffset(m_slice->m_start);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CMemPool(dk::Device dev, uint32_t flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached, uint32_t blockSize = DefaultBlockSize) :
|
||||||
|
m_dev{dev}, m_flags{flags}, m_blockSize{blockSize}, m_blocks{}, m_memMap{}, m_sliceHeap{}, m_freeList{} { }
|
||||||
|
~CMemPool();
|
||||||
|
|
||||||
|
Handle allocate(uint32_t size, uint32_t alignment = DK_CMDMEM_ALIGNMENT);
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr bool operator<(uint32_t lhs, CMemPool::Slice const& rhs)
|
||||||
|
{
|
||||||
|
return lhs < rhs.getSize();
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CShader.h: Utility class for loading shaders from the filesystem
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "common.h"
|
||||||
|
#include "CMemPool.h"
|
||||||
|
|
||||||
|
class CShader
|
||||||
|
{
|
||||||
|
dk::Shader m_shader;
|
||||||
|
CMemPool::Handle m_codemem;
|
||||||
|
public:
|
||||||
|
CShader() : m_shader{}, m_codemem{} { }
|
||||||
|
~CShader()
|
||||||
|
{
|
||||||
|
m_codemem.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr operator bool() const
|
||||||
|
{
|
||||||
|
return m_codemem;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr operator dk::Shader const*() const
|
||||||
|
{
|
||||||
|
return &m_shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool load(CMemPool& pool, const char* path);
|
||||||
|
};
|
|
@ -0,0 +1,9 @@
|
||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** FileLoader.h: Helpers for loading data from the filesystem directly into GPU memory
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include "common.h"
|
||||||
|
#include "CMemPool.h"
|
||||||
|
|
||||||
|
CMemPool::Handle LoadFile(CMemPool& pool, const char* path, uint32_t alignment = DK_CMDMEM_ALIGNMENT);
|
|
@ -0,0 +1,12 @@
|
||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** common.h: Common includes
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
#include <deko3d.hpp>
|
158
troposphere/daybreak/nanovg/include/nanovg/nanovg_gl_utils.h
Normal file
158
troposphere/daybreak/nanovg/include/nanovg/nanovg_gl_utils.h
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
//
|
||||||
|
// Copyright (c) 2009-2013 Mikko Mononen memon@inside.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 NANOVG_GL_UTILS_H
|
||||||
|
#define NANOVG_GL_UTILS_H
|
||||||
|
|
||||||
|
#ifdef USE_OPENGL
|
||||||
|
|
||||||
|
struct NVGLUframebuffer {
|
||||||
|
NVGcontext* ctx;
|
||||||
|
GLuint fbo;
|
||||||
|
GLuint rbo;
|
||||||
|
GLuint texture;
|
||||||
|
int image;
|
||||||
|
};
|
||||||
|
typedef struct NVGLUframebuffer NVGLUframebuffer;
|
||||||
|
|
||||||
|
// Helper function to create GL frame buffer to render to.
|
||||||
|
void nvgluBindFramebuffer(NVGLUframebuffer* fb);
|
||||||
|
NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags);
|
||||||
|
void nvgluDeleteFramebuffer(NVGLUframebuffer* fb);
|
||||||
|
|
||||||
|
#endif // NANOVG_GL_UTILS_H
|
||||||
|
|
||||||
|
#ifdef NANOVG_GL_IMPLEMENTATION
|
||||||
|
|
||||||
|
#if defined(NANOVG_GL3) || defined(NANOVG_GLES2) || defined(NANOVG_GLES3)
|
||||||
|
// FBO is core in OpenGL 3>.
|
||||||
|
# define NANOVG_FBO_VALID 1
|
||||||
|
#elif defined(NANOVG_GL2)
|
||||||
|
// On OS X including glext defines FBO on GL2 too.
|
||||||
|
# ifdef __APPLE__
|
||||||
|
# include <OpenGL/glext.h>
|
||||||
|
# define NANOVG_FBO_VALID 1
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static GLint defaultFBO = -1;
|
||||||
|
|
||||||
|
NVGLUframebuffer* nvgluCreateFramebuffer(NVGcontext* ctx, int w, int h, int imageFlags)
|
||||||
|
{
|
||||||
|
#ifdef NANOVG_FBO_VALID
|
||||||
|
GLint defaultFBO;
|
||||||
|
GLint defaultRBO;
|
||||||
|
NVGLUframebuffer* fb = NULL;
|
||||||
|
|
||||||
|
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO);
|
||||||
|
glGetIntegerv(GL_RENDERBUFFER_BINDING, &defaultRBO);
|
||||||
|
|
||||||
|
fb = (NVGLUframebuffer*)malloc(sizeof(NVGLUframebuffer));
|
||||||
|
if (fb == NULL) goto error;
|
||||||
|
memset(fb, 0, sizeof(NVGLUframebuffer));
|
||||||
|
|
||||||
|
fb->image = nvgCreateImageRGBA(ctx, w, h, imageFlags | NVG_IMAGE_FLIPY | NVG_IMAGE_PREMULTIPLIED, NULL);
|
||||||
|
|
||||||
|
#if defined NANOVG_GL2
|
||||||
|
fb->texture = nvglImageHandleGL2(ctx, fb->image);
|
||||||
|
#elif defined NANOVG_GL3
|
||||||
|
fb->texture = nvglImageHandleGL3(ctx, fb->image);
|
||||||
|
#elif defined NANOVG_GLES2
|
||||||
|
fb->texture = nvglImageHandleGLES2(ctx, fb->image);
|
||||||
|
#elif defined NANOVG_GLES3
|
||||||
|
fb->texture = nvglImageHandleGLES3(ctx, fb->image);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
fb->ctx = ctx;
|
||||||
|
|
||||||
|
// frame buffer object
|
||||||
|
glGenFramebuffers(1, &fb->fbo);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fb->fbo);
|
||||||
|
|
||||||
|
// render buffer object
|
||||||
|
glGenRenderbuffers(1, &fb->rbo);
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, fb->rbo);
|
||||||
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, w, h);
|
||||||
|
|
||||||
|
// combine all
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0);
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo);
|
||||||
|
|
||||||
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||||
|
#ifdef GL_DEPTH24_STENCIL8
|
||||||
|
// If GL_STENCIL_INDEX8 is not supported, try GL_DEPTH24_STENCIL8 as a fallback.
|
||||||
|
// Some graphics cards require a depth buffer along with a stencil.
|
||||||
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h);
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->texture, 0);
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb->rbo);
|
||||||
|
|
||||||
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||||
|
#endif // GL_DEPTH24_STENCIL8
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO);
|
||||||
|
return fb;
|
||||||
|
error:
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, defaultRBO);
|
||||||
|
nvgluDeleteFramebuffer(fb);
|
||||||
|
return NULL;
|
||||||
|
#else
|
||||||
|
NVG_NOTUSED(ctx);
|
||||||
|
NVG_NOTUSED(w);
|
||||||
|
NVG_NOTUSED(h);
|
||||||
|
NVG_NOTUSED(imageFlags);
|
||||||
|
return NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void nvgluBindFramebuffer(NVGLUframebuffer* fb)
|
||||||
|
{
|
||||||
|
#ifdef NANOVG_FBO_VALID
|
||||||
|
if (defaultFBO == -1) glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, fb != NULL ? fb->fbo : defaultFBO);
|
||||||
|
#else
|
||||||
|
NVG_NOTUSED(fb);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void nvgluDeleteFramebuffer(NVGLUframebuffer* fb)
|
||||||
|
{
|
||||||
|
#ifdef NANOVG_FBO_VALID
|
||||||
|
if (fb == NULL) return;
|
||||||
|
if (fb->fbo != 0)
|
||||||
|
glDeleteFramebuffers(1, &fb->fbo);
|
||||||
|
if (fb->rbo != 0)
|
||||||
|
glDeleteRenderbuffers(1, &fb->rbo);
|
||||||
|
if (fb->image >= 0)
|
||||||
|
nvgDeleteImage(fb->ctx, fb->image);
|
||||||
|
fb->ctx = NULL;
|
||||||
|
fb->fbo = 0;
|
||||||
|
fb->rbo = 0;
|
||||||
|
fb->texture = 0;
|
||||||
|
fb->image = -1;
|
||||||
|
free(fb);
|
||||||
|
#else
|
||||||
|
NVG_NOTUSED(fb);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // NANOVG_GL_IMPLEMENTATION
|
6614
troposphere/daybreak/nanovg/include/nanovg/stb_image.h
Normal file
6614
troposphere/daybreak/nanovg/include/nanovg/stb_image.h
Normal file
File diff suppressed because it is too large
Load diff
5011
troposphere/daybreak/nanovg/include/nanovg/stb_truetype.h
Normal file
5011
troposphere/daybreak/nanovg/include/nanovg/stb_truetype.h
Normal file
File diff suppressed because it is too large
Load diff
520
troposphere/daybreak/nanovg/include/nanovg_dk.h
Normal file
520
troposphere/daybreak/nanovg/include/nanovg_dk.h
Normal file
|
@ -0,0 +1,520 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "nanovg.h"
|
||||||
|
#include "nanovg/dk_renderer.hpp"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int dknvg__maxi(int a, int b) { return a > b ? a : b; }
|
||||||
|
|
||||||
|
static const DKNVGtextureDescriptor* dknvg__findTexture(DKNVGcontext* dk, int id) {
|
||||||
|
return dk->renderer->GetTextureDescriptor(*dk, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dknvg__renderCreate(void* uptr)
|
||||||
|
{
|
||||||
|
DKNVGcontext *dk = (DKNVGcontext*)uptr;
|
||||||
|
return dk->renderer->Create(*dk);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dknvg__renderCreateTexture(void* uptr, int type, int w, int h, int imageFlags, const unsigned char* data)
|
||||||
|
{
|
||||||
|
DKNVGcontext *dk = (DKNVGcontext*)uptr;
|
||||||
|
return dk->renderer->CreateTexture(*dk, type, w, h, imageFlags, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dknvg__renderDeleteTexture(void* uptr, int image) {
|
||||||
|
DKNVGcontext *dk = (DKNVGcontext*)uptr;
|
||||||
|
return dk->renderer->DeleteTexture(*dk, image);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dknvg__renderUpdateTexture(void* uptr, int image, int x, int y, int w, int h, const unsigned char* data) {
|
||||||
|
DKNVGcontext *dk = (DKNVGcontext*)uptr;
|
||||||
|
return dk->renderer->UpdateTexture(*dk, image, x, y, w, h, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dknvg__renderGetTextureSize(void* uptr, int image, int* w, int* h) {
|
||||||
|
DKNVGcontext *dk = (DKNVGcontext*)uptr;
|
||||||
|
return dk->renderer->GetTextureSize(*dk, image, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dknvg__xformToMat3x4(float* m3, float* t) {
|
||||||
|
m3[0] = t[0];
|
||||||
|
m3[1] = t[1];
|
||||||
|
m3[2] = 0.0f;
|
||||||
|
m3[3] = 0.0f;
|
||||||
|
m3[4] = t[2];
|
||||||
|
m3[5] = t[3];
|
||||||
|
m3[6] = 0.0f;
|
||||||
|
m3[7] = 0.0f;
|
||||||
|
m3[8] = t[4];
|
||||||
|
m3[9] = t[5];
|
||||||
|
m3[10] = 1.0f;
|
||||||
|
m3[11] = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NVGcolor dknvg__premulColor(NVGcolor c) {
|
||||||
|
c.r *= c.a;
|
||||||
|
c.g *= c.a;
|
||||||
|
c.b *= c.a;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dknvg__convertPaint(DKNVGcontext* dk, DKNVGfragUniforms* frag, NVGpaint* paint,
|
||||||
|
NVGscissor* scissor, float width, float fringe, float strokeThr)
|
||||||
|
{
|
||||||
|
const DKNVGtextureDescriptor *tex = NULL;
|
||||||
|
float invxform[6];
|
||||||
|
|
||||||
|
memset(frag, 0, sizeof(*frag));
|
||||||
|
|
||||||
|
frag->innerCol = dknvg__premulColor(paint->innerColor);
|
||||||
|
frag->outerCol = dknvg__premulColor(paint->outerColor);
|
||||||
|
|
||||||
|
if (scissor->extent[0] < -0.5f || scissor->extent[1] < -0.5f) {
|
||||||
|
memset(frag->scissorMat, 0, sizeof(frag->scissorMat));
|
||||||
|
frag->scissorExt[0] = 1.0f;
|
||||||
|
frag->scissorExt[1] = 1.0f;
|
||||||
|
frag->scissorScale[0] = 1.0f;
|
||||||
|
frag->scissorScale[1] = 1.0f;
|
||||||
|
} else {
|
||||||
|
nvgTransformInverse(invxform, scissor->xform);
|
||||||
|
dknvg__xformToMat3x4(frag->scissorMat, invxform);
|
||||||
|
frag->scissorExt[0] = scissor->extent[0];
|
||||||
|
frag->scissorExt[1] = scissor->extent[1];
|
||||||
|
frag->scissorScale[0] = sqrtf(scissor->xform[0]*scissor->xform[0] + scissor->xform[2]*scissor->xform[2]) / fringe;
|
||||||
|
frag->scissorScale[1] = sqrtf(scissor->xform[1]*scissor->xform[1] + scissor->xform[3]*scissor->xform[3]) / fringe;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(frag->extent, paint->extent, sizeof(frag->extent));
|
||||||
|
frag->strokeMult = (width*0.5f + fringe*0.5f) / fringe;
|
||||||
|
frag->strokeThr = strokeThr;
|
||||||
|
|
||||||
|
if (paint->image != 0) {
|
||||||
|
tex = dknvg__findTexture(dk, paint->image);
|
||||||
|
if (tex == NULL) return 0;
|
||||||
|
if ((tex->flags & NVG_IMAGE_FLIPY) != 0) {
|
||||||
|
float m1[6], m2[6];
|
||||||
|
nvgTransformTranslate(m1, 0.0f, frag->extent[1] * 0.5f);
|
||||||
|
nvgTransformMultiply(m1, paint->xform);
|
||||||
|
nvgTransformScale(m2, 1.0f, -1.0f);
|
||||||
|
nvgTransformMultiply(m2, m1);
|
||||||
|
nvgTransformTranslate(m1, 0.0f, -frag->extent[1] * 0.5f);
|
||||||
|
nvgTransformMultiply(m1, m2);
|
||||||
|
nvgTransformInverse(invxform, m1);
|
||||||
|
} else {
|
||||||
|
nvgTransformInverse(invxform, paint->xform);
|
||||||
|
}
|
||||||
|
frag->type = NSVG_SHADER_FILLIMG;
|
||||||
|
|
||||||
|
if (tex->type == NVG_TEXTURE_RGBA)
|
||||||
|
frag->texType = (tex->flags & NVG_IMAGE_PREMULTIPLIED) ? 0 : 1;
|
||||||
|
else
|
||||||
|
frag->texType = 2;
|
||||||
|
// printf("frag->texType = %d\n", frag->texType);
|
||||||
|
} else {
|
||||||
|
frag->type = NSVG_SHADER_FILLGRAD;
|
||||||
|
frag->radius = paint->radius;
|
||||||
|
frag->feather = paint->feather;
|
||||||
|
nvgTransformInverse(invxform, paint->xform);
|
||||||
|
}
|
||||||
|
|
||||||
|
dknvg__xformToMat3x4(frag->paintMat, invxform);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DKNVGfragUniforms* nvg__fragUniformPtr(DKNVGcontext* dk, int i);
|
||||||
|
|
||||||
|
static void dknvg__renderViewport(void* uptr, float width, float height, float devicePixelRatio)
|
||||||
|
{
|
||||||
|
NVG_NOTUSED(devicePixelRatio);
|
||||||
|
DKNVGcontext* dk = (DKNVGcontext*)uptr;
|
||||||
|
dk->view[0] = width;
|
||||||
|
dk->view[1] = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dknvg__renderCancel(void* uptr) {
|
||||||
|
DKNVGcontext* dk = (DKNVGcontext*)uptr;
|
||||||
|
dk->nverts = 0;
|
||||||
|
dk->npaths = 0;
|
||||||
|
dk->ncalls = 0;
|
||||||
|
dk->nuniforms = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dknvg_convertBlendFuncFactor(int factor) {
|
||||||
|
switch (factor) {
|
||||||
|
case NVG_ZERO:
|
||||||
|
return DkBlendFactor_Zero;
|
||||||
|
case NVG_ONE:
|
||||||
|
return DkBlendFactor_One;
|
||||||
|
case NVG_SRC_COLOR:
|
||||||
|
return DkBlendFactor_SrcColor;
|
||||||
|
case NVG_ONE_MINUS_SRC_COLOR:
|
||||||
|
return DkBlendFactor_InvSrcColor;
|
||||||
|
case NVG_DST_COLOR:
|
||||||
|
return DkBlendFactor_DstColor;
|
||||||
|
case NVG_ONE_MINUS_DST_COLOR:
|
||||||
|
return DkBlendFactor_InvDstColor;
|
||||||
|
case NVG_SRC_ALPHA:
|
||||||
|
return DkBlendFactor_SrcAlpha;
|
||||||
|
case NVG_ONE_MINUS_SRC_ALPHA:
|
||||||
|
return DkBlendFactor_InvSrcAlpha;
|
||||||
|
case NVG_DST_ALPHA:
|
||||||
|
return DkBlendFactor_DstAlpha;
|
||||||
|
case NVG_ONE_MINUS_DST_ALPHA:
|
||||||
|
return DkBlendFactor_InvDstAlpha;
|
||||||
|
case NVG_SRC_ALPHA_SATURATE:
|
||||||
|
return DkBlendFactor_SrcAlphaSaturate;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static DKNVGblend dknvg__blendCompositeOperation(NVGcompositeOperationState op) {
|
||||||
|
DKNVGblend blend;
|
||||||
|
blend.srcRGB = dknvg_convertBlendFuncFactor(op.srcRGB);
|
||||||
|
blend.dstRGB = dknvg_convertBlendFuncFactor(op.dstRGB);
|
||||||
|
blend.srcAlpha = dknvg_convertBlendFuncFactor(op.srcAlpha);
|
||||||
|
blend.dstAlpha = dknvg_convertBlendFuncFactor(op.dstAlpha);
|
||||||
|
|
||||||
|
if (blend.srcRGB == -1 || blend.dstRGB == -1 || blend.srcAlpha == -1 || blend.dstAlpha == -1) {
|
||||||
|
blend.srcRGB = DkBlendFactor_One;
|
||||||
|
blend.dstRGB = DkBlendFactor_InvSrcAlpha;
|
||||||
|
blend.srcAlpha = DkBlendFactor_One;
|
||||||
|
blend.dstAlpha = DkBlendFactor_InvSrcAlpha;
|
||||||
|
}
|
||||||
|
return blend;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dknvg__renderFlush(void* uptr) {
|
||||||
|
DKNVGcontext *dk = (DKNVGcontext*)uptr;
|
||||||
|
dk->renderer->Flush(*dk);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dknvg__maxVertCount(const NVGpath* paths, int npaths) {
|
||||||
|
int i, count = 0;
|
||||||
|
for (i = 0; i < npaths; i++) {
|
||||||
|
count += paths[i].nfill;
|
||||||
|
count += paths[i].nstroke;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DKNVGcall* dknvg__allocCall(DKNVGcontext* dk)
|
||||||
|
{
|
||||||
|
DKNVGcall* ret = NULL;
|
||||||
|
if (dk->ncalls+1 > dk->ccalls) {
|
||||||
|
DKNVGcall* calls;
|
||||||
|
int ccalls = dknvg__maxi(dk->ncalls+1, 128) + dk->ccalls/2; // 1.5x Overallocate
|
||||||
|
calls = (DKNVGcall*)realloc(dk->calls, sizeof(DKNVGcall) * ccalls);
|
||||||
|
if (calls == NULL) return NULL;
|
||||||
|
dk->calls = calls;
|
||||||
|
dk->ccalls = ccalls;
|
||||||
|
}
|
||||||
|
ret = &dk->calls[dk->ncalls++];
|
||||||
|
memset(ret, 0, sizeof(DKNVGcall));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dknvg__allocPaths(DKNVGcontext* dk, int n)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
if (dk->npaths+n > dk->cpaths) {
|
||||||
|
DKNVGpath* paths;
|
||||||
|
int cpaths = dknvg__maxi(dk->npaths + n, 128) + dk->cpaths/2; // 1.5x Overallocate
|
||||||
|
paths = (DKNVGpath*)realloc(dk->paths, sizeof(DKNVGpath) * cpaths);
|
||||||
|
if (paths == NULL) return -1;
|
||||||
|
dk->paths = paths;
|
||||||
|
dk->cpaths = cpaths;
|
||||||
|
}
|
||||||
|
ret = dk->npaths;
|
||||||
|
dk->npaths += n;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dknvg__allocVerts(DKNVGcontext* dk, int n)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
if (dk->nverts+n > dk->cverts) {
|
||||||
|
NVGvertex* verts;
|
||||||
|
int cverts = dknvg__maxi(dk->nverts + n, 4096) + dk->cverts/2; // 1.5x Overallocate
|
||||||
|
verts = (NVGvertex*)realloc(dk->verts, sizeof(NVGvertex) * cverts);
|
||||||
|
if (verts == NULL) return -1;
|
||||||
|
dk->verts = verts;
|
||||||
|
dk->cverts = cverts;
|
||||||
|
}
|
||||||
|
ret = dk->nverts;
|
||||||
|
dk->nverts += n;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dknvg__allocFragUniforms(DKNVGcontext* dk, int n)
|
||||||
|
{
|
||||||
|
int ret = 0, structSize = dk->fragSize;
|
||||||
|
if (dk->nuniforms+n > dk->cuniforms) {
|
||||||
|
unsigned char* uniforms;
|
||||||
|
int cuniforms = dknvg__maxi(dk->nuniforms+n, 128) + dk->cuniforms/2; // 1.5x Overallocate
|
||||||
|
uniforms = (unsigned char*)realloc(dk->uniforms, structSize * cuniforms);
|
||||||
|
if (uniforms == NULL) return -1;
|
||||||
|
dk->uniforms = uniforms;
|
||||||
|
dk->cuniforms = cuniforms;
|
||||||
|
}
|
||||||
|
ret = dk->nuniforms * structSize;
|
||||||
|
dk->nuniforms += n;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DKNVGfragUniforms* nvg__fragUniformPtr(DKNVGcontext* dk, int i)
|
||||||
|
{
|
||||||
|
return (DKNVGfragUniforms*)&dk->uniforms[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dknvg__vset(NVGvertex* vtx, float x, float y, float u, float v)
|
||||||
|
{
|
||||||
|
vtx->x = x;
|
||||||
|
vtx->y = y;
|
||||||
|
vtx->u = u;
|
||||||
|
vtx->v = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dknvg__renderFill(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe,
|
||||||
|
const float* bounds, const NVGpath* paths, int npaths)
|
||||||
|
{
|
||||||
|
DKNVGcontext* dk = (DKNVGcontext*)uptr;
|
||||||
|
DKNVGcall* call = dknvg__allocCall(dk);
|
||||||
|
NVGvertex* quad;
|
||||||
|
DKNVGfragUniforms* frag;
|
||||||
|
int i, maxverts, offset;
|
||||||
|
|
||||||
|
if (call == NULL) return;
|
||||||
|
|
||||||
|
call->type = DKNVG_FILL;
|
||||||
|
call->triangleCount = 4;
|
||||||
|
call->pathOffset = dknvg__allocPaths(dk, npaths);
|
||||||
|
if (call->pathOffset == -1) goto error;
|
||||||
|
call->pathCount = npaths;
|
||||||
|
call->image = paint->image;
|
||||||
|
call->blendFunc = dknvg__blendCompositeOperation(compositeOperation);
|
||||||
|
|
||||||
|
if (npaths == 1 && paths[0].convex)
|
||||||
|
{
|
||||||
|
call->type = DKNVG_CONVEXFILL;
|
||||||
|
call->triangleCount = 0; // Bounding box fill quad not needed for convex fill
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate vertices for all the paths.
|
||||||
|
maxverts = dknvg__maxVertCount(paths, npaths) + call->triangleCount;
|
||||||
|
offset = dknvg__allocVerts(dk, maxverts);
|
||||||
|
if (offset == -1) goto error;
|
||||||
|
|
||||||
|
for (i = 0; i < npaths; i++) {
|
||||||
|
DKNVGpath* copy = &dk->paths[call->pathOffset + i];
|
||||||
|
const NVGpath* path = &paths[i];
|
||||||
|
memset(copy, 0, sizeof(DKNVGpath));
|
||||||
|
if (path->nfill > 0) {
|
||||||
|
copy->fillOffset = offset;
|
||||||
|
copy->fillCount = path->nfill;
|
||||||
|
memcpy(&dk->verts[offset], path->fill, sizeof(NVGvertex) * path->nfill);
|
||||||
|
offset += path->nfill;
|
||||||
|
}
|
||||||
|
if (path->nstroke > 0) {
|
||||||
|
copy->strokeOffset = offset;
|
||||||
|
copy->strokeCount = path->nstroke;
|
||||||
|
memcpy(&dk->verts[offset], path->stroke, sizeof(NVGvertex) * path->nstroke);
|
||||||
|
offset += path->nstroke;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup uniforms for draw calls
|
||||||
|
if (call->type == DKNVG_FILL) {
|
||||||
|
// Quad
|
||||||
|
call->triangleOffset = offset;
|
||||||
|
quad = &dk->verts[call->triangleOffset];
|
||||||
|
dknvg__vset(&quad[0], bounds[2], bounds[3], 0.5f, 1.0f);
|
||||||
|
dknvg__vset(&quad[1], bounds[2], bounds[1], 0.5f, 1.0f);
|
||||||
|
dknvg__vset(&quad[2], bounds[0], bounds[3], 0.5f, 1.0f);
|
||||||
|
dknvg__vset(&quad[3], bounds[0], bounds[1], 0.5f, 1.0f);
|
||||||
|
|
||||||
|
call->uniformOffset = dknvg__allocFragUniforms(dk, 2);
|
||||||
|
if (call->uniformOffset == -1) goto error;
|
||||||
|
// Simple shader for stencil
|
||||||
|
frag = nvg__fragUniformPtr(dk, call->uniformOffset);
|
||||||
|
memset(frag, 0, sizeof(*frag));
|
||||||
|
frag->strokeThr = -1.0f;
|
||||||
|
frag->type = NSVG_SHADER_SIMPLE;
|
||||||
|
// Fill shader
|
||||||
|
dknvg__convertPaint(dk, nvg__fragUniformPtr(dk, call->uniformOffset + dk->fragSize), paint, scissor, fringe, fringe, -1.0f);
|
||||||
|
} else {
|
||||||
|
call->uniformOffset = dknvg__allocFragUniforms(dk, 1);
|
||||||
|
if (call->uniformOffset == -1) goto error;
|
||||||
|
// Fill shader
|
||||||
|
dknvg__convertPaint(dk, nvg__fragUniformPtr(dk, call->uniformOffset), paint, scissor, fringe, fringe, -1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
error:
|
||||||
|
// We get here if call alloc was ok, but something else is not.
|
||||||
|
// Roll back the last call to prevent drawing it.
|
||||||
|
if (dk->ncalls > 0) dk->ncalls--;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dknvg__renderStroke(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor, float fringe,
|
||||||
|
float strokeWidth, const NVGpath* paths, int npaths)
|
||||||
|
{
|
||||||
|
DKNVGcontext* dk = (DKNVGcontext*)uptr;
|
||||||
|
DKNVGcall* call = dknvg__allocCall(dk);
|
||||||
|
int i, maxverts, offset;
|
||||||
|
|
||||||
|
if (call == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
call->type = DKNVG_STROKE;
|
||||||
|
call->pathOffset = dknvg__allocPaths(dk, npaths);
|
||||||
|
if (call->pathOffset == -1) goto error;
|
||||||
|
call->pathCount = npaths;
|
||||||
|
call->image = paint->image;
|
||||||
|
call->blendFunc = dknvg__blendCompositeOperation(compositeOperation);
|
||||||
|
|
||||||
|
// Allocate vertices for all the paths.
|
||||||
|
maxverts = dknvg__maxVertCount(paths, npaths);
|
||||||
|
offset = dknvg__allocVerts(dk, maxverts);
|
||||||
|
if (offset == -1) goto error;
|
||||||
|
|
||||||
|
for (i = 0; i < npaths; i++) {
|
||||||
|
DKNVGpath* copy = &dk->paths[call->pathOffset + i];
|
||||||
|
const NVGpath* path = &paths[i];
|
||||||
|
memset(copy, 0, sizeof(DKNVGpath));
|
||||||
|
if (path->nstroke) {
|
||||||
|
copy->strokeOffset = offset;
|
||||||
|
copy->strokeCount = path->nstroke;
|
||||||
|
memcpy(&dk->verts[offset], path->stroke, sizeof(NVGvertex) * path->nstroke);
|
||||||
|
offset += path->nstroke;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dk->flags & NVG_STENCIL_STROKES) {
|
||||||
|
// Fill shader
|
||||||
|
call->uniformOffset = dknvg__allocFragUniforms(dk, 2);
|
||||||
|
if (call->uniformOffset == -1) goto error;
|
||||||
|
|
||||||
|
dknvg__convertPaint(dk, nvg__fragUniformPtr(dk, call->uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f);
|
||||||
|
dknvg__convertPaint(dk, nvg__fragUniformPtr(dk, call->uniformOffset + dk->fragSize), paint, scissor, strokeWidth, fringe, 1.0f - 0.5f/255.0f);
|
||||||
|
} else {
|
||||||
|
// Fill shader
|
||||||
|
call->uniformOffset = dknvg__allocFragUniforms(dk, 1);
|
||||||
|
if (call->uniformOffset == -1) goto error;
|
||||||
|
|
||||||
|
dknvg__convertPaint(dk, nvg__fragUniformPtr(dk, call->uniformOffset), paint, scissor, strokeWidth, fringe, -1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
error:
|
||||||
|
// We get here if call alloc was ok, but something else is not.
|
||||||
|
// Roll back the last call to prevent drawing it.
|
||||||
|
if (dk->ncalls > 0) dk->ncalls--;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dknvg__renderTriangles(void* uptr, NVGpaint* paint, NVGcompositeOperationState compositeOperation, NVGscissor* scissor,
|
||||||
|
const NVGvertex* verts, int nverts, float fringe)
|
||||||
|
{
|
||||||
|
DKNVGcontext* dk = (DKNVGcontext*)uptr;
|
||||||
|
DKNVGcall* call = dknvg__allocCall(dk);
|
||||||
|
DKNVGfragUniforms* frag;
|
||||||
|
|
||||||
|
if (call == NULL) return;
|
||||||
|
|
||||||
|
call->type = DKNVG_TRIANGLES;
|
||||||
|
call->image = paint->image;
|
||||||
|
call->blendFunc = dknvg__blendCompositeOperation(compositeOperation);
|
||||||
|
|
||||||
|
// Allocate vertices for all the paths.
|
||||||
|
call->triangleOffset = dknvg__allocVerts(dk, nverts);
|
||||||
|
if (call->triangleOffset == -1) goto error;
|
||||||
|
call->triangleCount = nverts;
|
||||||
|
|
||||||
|
memcpy(&dk->verts[call->triangleOffset], verts, sizeof(NVGvertex) * nverts);
|
||||||
|
|
||||||
|
// Fill shader
|
||||||
|
call->uniformOffset = dknvg__allocFragUniforms(dk, 1);
|
||||||
|
if (call->uniformOffset == -1) goto error;
|
||||||
|
frag = nvg__fragUniformPtr(dk, call->uniformOffset);
|
||||||
|
dknvg__convertPaint(dk, frag, paint, scissor, 1.0f, fringe, -1.0f);
|
||||||
|
frag->type = NSVG_SHADER_IMG;
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
error:
|
||||||
|
// We get here if call alloc was ok, but something else is not.
|
||||||
|
// Roll back the last call to prevent drawing it.
|
||||||
|
if (dk->ncalls > 0) dk->ncalls--;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dknvg__renderDelete(void* uptr) {
|
||||||
|
DKNVGcontext* dk = (DKNVGcontext*)uptr;
|
||||||
|
if (dk == NULL) return;
|
||||||
|
|
||||||
|
free(dk->paths);
|
||||||
|
free(dk->verts);
|
||||||
|
free(dk->uniforms);
|
||||||
|
free(dk->calls);
|
||||||
|
|
||||||
|
free(dk);
|
||||||
|
}
|
||||||
|
|
||||||
|
NVGcontext* nvgCreateDk(nvg::DkRenderer *renderer, int flags) {
|
||||||
|
NVGparams params;
|
||||||
|
NVGcontext* ctx = NULL;
|
||||||
|
DKNVGcontext* dk = (DKNVGcontext*)malloc(sizeof(DKNVGcontext));
|
||||||
|
if (dk == NULL) goto error;
|
||||||
|
memset(dk, 0, sizeof(DKNVGcontext));
|
||||||
|
|
||||||
|
memset(¶ms, 0, sizeof(params));
|
||||||
|
params.renderCreate = dknvg__renderCreate;
|
||||||
|
params.renderCreateTexture = dknvg__renderCreateTexture;
|
||||||
|
params.renderDeleteTexture = dknvg__renderDeleteTexture;
|
||||||
|
params.renderUpdateTexture = dknvg__renderUpdateTexture;
|
||||||
|
params.renderGetTextureSize = dknvg__renderGetTextureSize;
|
||||||
|
params.renderViewport = dknvg__renderViewport;
|
||||||
|
params.renderCancel = dknvg__renderCancel;
|
||||||
|
params.renderFlush = dknvg__renderFlush;
|
||||||
|
params.renderFill = dknvg__renderFill;
|
||||||
|
params.renderStroke = dknvg__renderStroke;
|
||||||
|
params.renderTriangles = dknvg__renderTriangles;
|
||||||
|
params.renderDelete = dknvg__renderDelete;
|
||||||
|
params.userPtr = dk;
|
||||||
|
params.edgeAntiAlias = flags & NVG_ANTIALIAS ? 1 : 0;
|
||||||
|
|
||||||
|
dk->renderer = renderer;
|
||||||
|
dk->flags = flags;
|
||||||
|
|
||||||
|
ctx = nvgCreateInternal(¶ms);
|
||||||
|
if (ctx == NULL) goto error;
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
|
||||||
|
error:
|
||||||
|
// 'dk' is freed by nvgDeleteInternal.
|
||||||
|
if (ctx != NULL) nvgDeleteInternal(ctx);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nvgDeleteDk(NVGcontext* ctx)
|
||||||
|
{
|
||||||
|
nvgDeleteInternal(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
1672
troposphere/daybreak/nanovg/include/nanovg_gl.h
Normal file
1672
troposphere/daybreak/nanovg/include/nanovg_gl.h
Normal file
File diff suppressed because it is too large
Load diff
83
troposphere/daybreak/nanovg/shaders/fill_aa_fsh.glsl
Normal file
83
troposphere/daybreak/nanovg/shaders/fill_aa_fsh.glsl
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
#version 460
|
||||||
|
|
||||||
|
layout(binding = 0) uniform sampler2D tex;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform frag {
|
||||||
|
mat3 scissorMat;
|
||||||
|
mat3 paintMat;
|
||||||
|
vec4 innerCol;
|
||||||
|
vec4 outerCol;
|
||||||
|
vec2 scissorExt;
|
||||||
|
vec2 scissorScale;
|
||||||
|
vec2 extent;
|
||||||
|
float radius;
|
||||||
|
float feather;
|
||||||
|
float strokeMult;
|
||||||
|
float strokeThr;
|
||||||
|
int texType;
|
||||||
|
int type;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 ftcoord;
|
||||||
|
layout(location = 1) in vec2 fpos;
|
||||||
|
layout(location = 0) out vec4 outColor;
|
||||||
|
|
||||||
|
float sdroundrect(vec2 pt, vec2 ext, float rad) {
|
||||||
|
vec2 ext2 = ext - vec2(rad,rad);
|
||||||
|
vec2 d = abs(pt) - ext2;
|
||||||
|
return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rad;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scissoring
|
||||||
|
float scissorMask(vec2 p) {
|
||||||
|
vec2 sc = (abs((scissorMat * vec3(p,1.0)).xy) - scissorExt);
|
||||||
|
sc = vec2(0.5,0.5) - sc * scissorScale;
|
||||||
|
return clamp(sc.x,0.0,1.0) * clamp(sc.y,0.0,1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stroke - from [0..1] to clipped pyramid, where the slope is 1px.
|
||||||
|
float strokeMask() {
|
||||||
|
return min(1.0, (1.0-abs(ftcoord.x*2.0-1.0))*strokeMult) * min(1.0, ftcoord.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
vec4 result;
|
||||||
|
float scissor = scissorMask(fpos);
|
||||||
|
float strokeAlpha = strokeMask();
|
||||||
|
|
||||||
|
if (strokeAlpha < strokeThr) discard;
|
||||||
|
|
||||||
|
if (type == 0) { // Gradient
|
||||||
|
// Calculate gradient color using box gradient
|
||||||
|
vec2 pt = (paintMat * vec3(fpos,1.0)).xy;
|
||||||
|
float d = clamp((sdroundrect(pt, extent, radius) + feather*0.5) / feather, 0.0, 1.0);
|
||||||
|
vec4 color = mix(innerCol,outerCol,d);
|
||||||
|
// Combine alpha
|
||||||
|
color *= strokeAlpha * scissor;
|
||||||
|
result = color;
|
||||||
|
} else if (type == 1) { // Image
|
||||||
|
// Calculate color fron texture
|
||||||
|
vec2 pt = (paintMat * vec3(fpos,1.0)).xy / extent;
|
||||||
|
vec4 color = texture(tex, pt);
|
||||||
|
|
||||||
|
if (texType == 1) color = vec4(color.xyz*color.w,color.w);
|
||||||
|
if (texType == 2) color = vec4(color.x);
|
||||||
|
// Apply color tint and alpha.
|
||||||
|
color *= innerCol;
|
||||||
|
// Combine alpha
|
||||||
|
color *= strokeAlpha * scissor;
|
||||||
|
result = color;
|
||||||
|
} else if (type == 2) { // Stencil fill
|
||||||
|
result = vec4(1,1,1,1);
|
||||||
|
} else if (type == 3) { // Textured tris
|
||||||
|
|
||||||
|
vec4 color = texture(tex, ftcoord);
|
||||||
|
|
||||||
|
if (texType == 1) color = vec4(color.xyz*color.w,color.w);
|
||||||
|
if (texType == 2) color = vec4(color.x);
|
||||||
|
color *= scissor;
|
||||||
|
result = color * innerCol;
|
||||||
|
}
|
||||||
|
|
||||||
|
outColor = result;
|
||||||
|
};
|
76
troposphere/daybreak/nanovg/shaders/fill_fsh.glsl
Normal file
76
troposphere/daybreak/nanovg/shaders/fill_fsh.glsl
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
#version 460
|
||||||
|
|
||||||
|
layout(binding = 0) uniform sampler2D tex;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform frag {
|
||||||
|
mat3 scissorMat;
|
||||||
|
mat3 paintMat;
|
||||||
|
vec4 innerCol;
|
||||||
|
vec4 outerCol;
|
||||||
|
vec2 scissorExt;
|
||||||
|
vec2 scissorScale;
|
||||||
|
vec2 extent;
|
||||||
|
float radius;
|
||||||
|
float feather;
|
||||||
|
float strokeMult;
|
||||||
|
float strokeThr;
|
||||||
|
int texType;
|
||||||
|
int type;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 ftcoord;
|
||||||
|
layout(location = 1) in vec2 fpos;
|
||||||
|
layout(location = 0) out vec4 outColor;
|
||||||
|
|
||||||
|
float sdroundrect(vec2 pt, vec2 ext, float rad) {
|
||||||
|
vec2 ext2 = ext - vec2(rad,rad);
|
||||||
|
vec2 d = abs(pt) - ext2;
|
||||||
|
return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rad;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scissoring
|
||||||
|
float scissorMask(vec2 p) {
|
||||||
|
vec2 sc = (abs((scissorMat * vec3(p,1.0)).xy) - scissorExt);
|
||||||
|
sc = vec2(0.5,0.5) - sc * scissorScale;
|
||||||
|
return clamp(sc.x,0.0,1.0) * clamp(sc.y,0.0,1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
vec4 result;
|
||||||
|
float scissor = scissorMask(fpos);
|
||||||
|
float strokeAlpha = 1.0;
|
||||||
|
|
||||||
|
if (type == 0) { // Gradient
|
||||||
|
// Calculate gradient color using box gradient
|
||||||
|
vec2 pt = (paintMat * vec3(fpos,1.0)).xy;
|
||||||
|
float d = clamp((sdroundrect(pt, extent, radius) + feather*0.5) / feather, 0.0, 1.0);
|
||||||
|
vec4 color = mix(innerCol,outerCol,d);
|
||||||
|
// Combine alpha
|
||||||
|
color *= strokeAlpha * scissor;
|
||||||
|
result = color;
|
||||||
|
} else if (type == 1) { // Image
|
||||||
|
// Calculate color fron texture
|
||||||
|
vec2 pt = (paintMat * vec3(fpos,1.0)).xy / extent;
|
||||||
|
vec4 color = texture(tex, pt);
|
||||||
|
|
||||||
|
if (texType == 1) color = vec4(color.xyz*color.w,color.w);
|
||||||
|
if (texType == 2) color = vec4(color.x);
|
||||||
|
// Apply color tint and alpha.
|
||||||
|
color *= innerCol;
|
||||||
|
// Combine alpha
|
||||||
|
color *= strokeAlpha * scissor;
|
||||||
|
result = color;
|
||||||
|
} else if (type == 2) { // Stencil fill
|
||||||
|
result = vec4(1,1,1,1);
|
||||||
|
} else if (type == 3) { // Textured tris
|
||||||
|
|
||||||
|
vec4 color = texture(tex, ftcoord);
|
||||||
|
|
||||||
|
if (texType == 1) color = vec4(color.xyz*color.w,color.w);
|
||||||
|
if (texType == 2) color = vec4(color.x);
|
||||||
|
color *= scissor;
|
||||||
|
result = color * innerCol;
|
||||||
|
}
|
||||||
|
|
||||||
|
outColor = result;
|
||||||
|
};
|
17
troposphere/daybreak/nanovg/shaders/fill_vsh.glsl
Normal file
17
troposphere/daybreak/nanovg/shaders/fill_vsh.glsl
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#version 460
|
||||||
|
|
||||||
|
layout (location = 0) in vec2 vertex;
|
||||||
|
layout (location = 1) in vec2 tcoord;
|
||||||
|
layout (location = 0) out vec2 ftcoord;
|
||||||
|
layout (location = 1) out vec2 fpos;
|
||||||
|
|
||||||
|
layout (std140, binding = 0) uniform View
|
||||||
|
{
|
||||||
|
vec2 size;
|
||||||
|
} view;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
ftcoord = tcoord;
|
||||||
|
fpos = vertex;
|
||||||
|
gl_Position = vec4(2.0*vertex.x/view.size.x - 1.0, 1.0 - 2.0*vertex.y/view.size.y, 0, 1);
|
||||||
|
};
|
545
troposphere/daybreak/nanovg/source/dk_renderer.cpp
Normal file
545
troposphere/daybreak/nanovg/source/dk_renderer.cpp
Normal file
|
@ -0,0 +1,545 @@
|
||||||
|
#include "dk_renderer.hpp"
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
#define GLM_FORCE_DEFAULT_ALIGNED_GENTYPES /* Enforces GLSL std140/std430 alignment rules for glm types. */
|
||||||
|
#define GLM_FORCE_INTRINSICS /* Enables usage of SIMD CPU instructions (requiring the above as well). */
|
||||||
|
#include <glm/vec2.hpp>
|
||||||
|
|
||||||
|
namespace nvg {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr std::array VertexBufferState = { DkVtxBufferState{sizeof(NVGvertex), 0}, };
|
||||||
|
|
||||||
|
constexpr std::array VertexAttribState = {
|
||||||
|
DkVtxAttribState{0, 0, offsetof(NVGvertex, x), DkVtxAttribSize_2x32, DkVtxAttribType_Float, 0},
|
||||||
|
DkVtxAttribState{0, 0, offsetof(NVGvertex, u), DkVtxAttribSize_2x32, DkVtxAttribType_Float, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
struct View {
|
||||||
|
glm::vec2 size;
|
||||||
|
};
|
||||||
|
|
||||||
|
void UpdateImage(dk::Image &image, CMemPool &scratchPool, dk::Device device, dk::Queue transferQueue, int type, int x, int y, int w, int h, const u8 *data) {
|
||||||
|
/* Do not proceed if no data is provided upfront. */
|
||||||
|
if (data == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate memory from the pool for the image. */
|
||||||
|
const size_t imageSize = type == NVG_TEXTURE_RGBA ? w * h * 4 : w * h;
|
||||||
|
CMemPool::Handle tempimgmem = scratchPool.allocate(imageSize, DK_IMAGE_LINEAR_STRIDE_ALIGNMENT);
|
||||||
|
memcpy(tempimgmem.getCpuAddr(), data, imageSize);
|
||||||
|
|
||||||
|
dk::UniqueCmdBuf tempcmdbuf = dk::CmdBufMaker{device}.create();
|
||||||
|
CMemPool::Handle tempcmdmem = scratchPool.allocate(DK_MEMBLOCK_ALIGNMENT);
|
||||||
|
tempcmdbuf.addMemory(tempcmdmem.getMemBlock(), tempcmdmem.getOffset(), tempcmdmem.getSize());
|
||||||
|
|
||||||
|
dk::ImageView imageView{image};
|
||||||
|
tempcmdbuf.copyBufferToImage({ tempimgmem.getGpuAddr() }, imageView, { static_cast<uint32_t>(x), static_cast<uint32_t>(y), 0, static_cast<uint32_t>(w), static_cast<uint32_t>(h), 1 });
|
||||||
|
|
||||||
|
transferQueue.submitCommands(tempcmdbuf.finishList());
|
||||||
|
transferQueue.waitIdle();
|
||||||
|
|
||||||
|
/* Destroy temp mem. */
|
||||||
|
tempcmdmem.destroy();
|
||||||
|
tempimgmem.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture::Texture(int id) : m_id(id) { /* ... */ }
|
||||||
|
|
||||||
|
Texture::~Texture() {
|
||||||
|
m_image_mem.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::Initialize(CMemPool &image_pool, CMemPool &scratch_pool, dk::Device device, dk::Queue queue, int type, int w, int h, int image_flags, const u8 *data) {
|
||||||
|
m_texture_descriptor = {
|
||||||
|
.width = w,
|
||||||
|
.height = h,
|
||||||
|
.type = type,
|
||||||
|
.flags = image_flags,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Create an image layout. */
|
||||||
|
dk::ImageLayout layout;
|
||||||
|
auto layout_maker = dk::ImageLayoutMaker{device}.setFlags(0).setDimensions(w, h);
|
||||||
|
if (type == NVG_TEXTURE_RGBA) {
|
||||||
|
layout_maker.setFormat(DkImageFormat_RGBA8_Unorm);
|
||||||
|
} else {
|
||||||
|
layout_maker.setFormat(DkImageFormat_R8_Unorm);
|
||||||
|
}
|
||||||
|
layout_maker.initialize(layout);
|
||||||
|
|
||||||
|
/* Initialize image. */
|
||||||
|
m_image_mem = image_pool.allocate(layout.getSize(), layout.getAlignment());
|
||||||
|
m_image.initialize(layout, m_image_mem.getMemBlock(), m_image_mem.getOffset());
|
||||||
|
m_image_descriptor.initialize(m_image);
|
||||||
|
|
||||||
|
/* Only update the image if the data isn't null. */
|
||||||
|
if (data != nullptr) {
|
||||||
|
UpdateImage(m_image, scratch_pool, device, queue, type, 0, 0, w, h, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Texture::GetId() {
|
||||||
|
return m_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DKNVGtextureDescriptor &Texture::GetDescriptor() {
|
||||||
|
return m_texture_descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
dk::Image &Texture::GetImage() {
|
||||||
|
return m_image;
|
||||||
|
}
|
||||||
|
|
||||||
|
dk::ImageDescriptor &Texture::GetImageDescriptor() {
|
||||||
|
return m_image_descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
DkRenderer::DkRenderer(unsigned int view_width, unsigned int view_height, dk::Device device, dk::Queue queue, CMemPool &image_mem_pool, CMemPool &code_mem_pool, CMemPool &data_mem_pool) :
|
||||||
|
m_view_width(view_width), m_view_height(view_height), m_device(device), m_queue(queue), m_image_mem_pool(image_mem_pool), m_code_mem_pool(code_mem_pool), m_data_mem_pool(data_mem_pool), m_image_descriptor_mappings({0})
|
||||||
|
{
|
||||||
|
/* Create a dynamic command buffer and allocate memory for it. */
|
||||||
|
m_dyn_cmd_buf = dk::CmdBufMaker{m_device}.create();
|
||||||
|
m_dyn_cmd_mem.allocate(m_data_mem_pool, DynamicCmdSize);
|
||||||
|
|
||||||
|
m_image_descriptor_set.allocate(m_data_mem_pool);
|
||||||
|
m_sampler_descriptor_set.allocate(m_data_mem_pool);
|
||||||
|
|
||||||
|
m_view_uniform_buffer = m_data_mem_pool.allocate(sizeof(View), DK_UNIFORM_BUF_ALIGNMENT);
|
||||||
|
m_frag_uniform_buffer = m_data_mem_pool.allocate(sizeof(FragmentUniformSize), DK_UNIFORM_BUF_ALIGNMENT);
|
||||||
|
|
||||||
|
/* Create and bind preset samplers. */
|
||||||
|
dk::UniqueCmdBuf init_cmd_buf = dk::CmdBufMaker{m_device}.create();
|
||||||
|
CMemPool::Handle init_cmd_mem = m_data_mem_pool.allocate(DK_MEMBLOCK_ALIGNMENT);
|
||||||
|
init_cmd_buf.addMemory(init_cmd_mem.getMemBlock(), init_cmd_mem.getOffset(), init_cmd_mem.getSize());
|
||||||
|
|
||||||
|
for (u8 i = 0; i < SamplerType_Total; i++) {
|
||||||
|
const DkFilter filter = (i & SamplerType_Nearest) ? DkFilter_Nearest : DkFilter_Linear;
|
||||||
|
const DkMipFilter mip_filter = (i & SamplerType_Nearest) ? DkMipFilter_Nearest : DkMipFilter_Linear;
|
||||||
|
const DkWrapMode u_wrap_mode = (i & SamplerType_RepeatX) ? DkWrapMode_Repeat : DkWrapMode_ClampToEdge;
|
||||||
|
const DkWrapMode v_wrap_mode = (i & SamplerType_RepeatY) ? DkWrapMode_Repeat : DkWrapMode_ClampToEdge;
|
||||||
|
|
||||||
|
auto sampler = dk::Sampler{};
|
||||||
|
auto sampler_descriptor = dk::SamplerDescriptor{};
|
||||||
|
sampler.setFilter(filter, filter, (i & SamplerType_MipFilter) ? mip_filter : DkMipFilter_None);
|
||||||
|
sampler.setWrapMode(u_wrap_mode, v_wrap_mode);
|
||||||
|
sampler_descriptor.initialize(sampler);
|
||||||
|
m_sampler_descriptor_set.update(init_cmd_buf, i, sampler_descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Flush the descriptor cache. */
|
||||||
|
init_cmd_buf.barrier(DkBarrier_None, DkInvalidateFlags_Descriptors);
|
||||||
|
|
||||||
|
m_sampler_descriptor_set.bindForSamplers(init_cmd_buf);
|
||||||
|
m_image_descriptor_set.bindForImages(init_cmd_buf);
|
||||||
|
|
||||||
|
m_queue.submitCommands(init_cmd_buf.finishList());
|
||||||
|
m_queue.waitIdle();
|
||||||
|
|
||||||
|
init_cmd_mem.destroy();
|
||||||
|
init_cmd_buf.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
DkRenderer::~DkRenderer() {
|
||||||
|
if (m_vertex_buffer) {
|
||||||
|
m_vertex_buffer->destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_view_uniform_buffer.destroy();
|
||||||
|
m_frag_uniform_buffer.destroy();
|
||||||
|
m_textures.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
int DkRenderer::AcquireImageDescriptor(std::shared_ptr<Texture> texture, int image) {
|
||||||
|
int free_image_descriptor = m_last_image_descriptor + 1;
|
||||||
|
int mapping = 0;
|
||||||
|
|
||||||
|
for (int desc = 0; desc <= m_last_image_descriptor; desc++) {
|
||||||
|
mapping = m_image_descriptor_mappings[desc];
|
||||||
|
|
||||||
|
/* We've found the image descriptor requested. */
|
||||||
|
if (mapping == image) {
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the free image descriptor. */
|
||||||
|
if (mapping == 0 && free_image_descriptor == m_last_image_descriptor + 1) {
|
||||||
|
free_image_descriptor = desc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No descriptors are free. */
|
||||||
|
if (free_image_descriptor >= static_cast<int>(MaxImages)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update descriptor sets. */
|
||||||
|
m_image_descriptor_set.update(m_dyn_cmd_buf, free_image_descriptor, texture->GetImageDescriptor());
|
||||||
|
|
||||||
|
/* Flush the descriptor cache. */
|
||||||
|
m_dyn_cmd_buf.barrier(DkBarrier_None, DkInvalidateFlags_Descriptors);
|
||||||
|
|
||||||
|
/* Update the map. */
|
||||||
|
m_image_descriptor_mappings[free_image_descriptor] = image;
|
||||||
|
m_last_image_descriptor = free_image_descriptor;
|
||||||
|
return free_image_descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DkRenderer::FreeImageDescriptor(int image) {
|
||||||
|
for (int desc = 0; desc <= m_last_image_descriptor; desc++) {
|
||||||
|
if (m_image_descriptor_mappings[desc] == image) {
|
||||||
|
m_image_descriptor_mappings[desc] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DkRenderer::UpdateVertexBuffer(const void *data, size_t size) {
|
||||||
|
/* Destroy the existing vertex buffer if it is too small. */
|
||||||
|
if (m_vertex_buffer && m_vertex_buffer->getSize() < size) {
|
||||||
|
m_vertex_buffer->destroy();
|
||||||
|
m_vertex_buffer.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create a new buffer if needed. */
|
||||||
|
if (!m_vertex_buffer) {
|
||||||
|
m_vertex_buffer = m_data_mem_pool.allocate(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy data to the vertex buffer if it exists. */
|
||||||
|
if (m_vertex_buffer) {
|
||||||
|
memcpy(m_vertex_buffer->getCpuAddr(), data, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DkRenderer::SetUniforms(const DKNVGcontext &ctx, int offset, int image) {
|
||||||
|
m_dyn_cmd_buf.pushConstants(m_frag_uniform_buffer.getGpuAddr(), m_frag_uniform_buffer.getSize(), 0, ctx.fragSize, ctx.uniforms + offset);
|
||||||
|
m_dyn_cmd_buf.bindUniformBuffer(DkStage_Fragment, 0, m_frag_uniform_buffer.getGpuAddr(), m_frag_uniform_buffer.getSize());
|
||||||
|
|
||||||
|
/* Attempt to find a texture. */
|
||||||
|
const auto texture = this->FindTexture(image);
|
||||||
|
if (texture == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Acquire an image descriptor. */
|
||||||
|
const int image_desc_id = this->AcquireImageDescriptor(texture, image);
|
||||||
|
if (image_desc_id == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int image_flags = texture->GetDescriptor().flags;
|
||||||
|
uint32_t sampler_id = 0;
|
||||||
|
|
||||||
|
if (image_flags & NVG_IMAGE_GENERATE_MIPMAPS) sampler_id |= SamplerType_MipFilter;
|
||||||
|
if (image_flags & NVG_IMAGE_NEAREST) sampler_id |= SamplerType_Nearest;
|
||||||
|
if (image_flags & NVG_IMAGE_REPEATX) sampler_id |= SamplerType_RepeatX;
|
||||||
|
if (image_flags & NVG_IMAGE_REPEATY) sampler_id |= SamplerType_RepeatY;
|
||||||
|
|
||||||
|
m_dyn_cmd_buf.bindTextures(DkStage_Fragment, 0, dkMakeTextureHandle(image_desc_id, sampler_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DkRenderer::DrawFill(const DKNVGcontext &ctx, const DKNVGcall &call) {
|
||||||
|
DKNVGpath *paths = &ctx.paths[call.pathOffset];
|
||||||
|
int npaths = call.pathCount;
|
||||||
|
|
||||||
|
/* Set the stencils to be used. */
|
||||||
|
m_dyn_cmd_buf.setStencil(DkFace_FrontAndBack, 0xFF, 0x0, 0xFF);
|
||||||
|
|
||||||
|
/* Set the depth stencil state. */
|
||||||
|
auto depth_stencil_state = dk::DepthStencilState{}
|
||||||
|
.setStencilTestEnable(true)
|
||||||
|
.setStencilFrontCompareOp(DkCompareOp_Always)
|
||||||
|
.setStencilFrontFailOp(DkStencilOp_Keep)
|
||||||
|
.setStencilFrontDepthFailOp(DkStencilOp_Keep)
|
||||||
|
.setStencilFrontPassOp(DkStencilOp_IncrWrap)
|
||||||
|
.setStencilBackCompareOp(DkCompareOp_Always)
|
||||||
|
.setStencilBackFailOp(DkStencilOp_Keep)
|
||||||
|
.setStencilBackDepthFailOp(DkStencilOp_Keep)
|
||||||
|
.setStencilBackPassOp(DkStencilOp_DecrWrap);
|
||||||
|
m_dyn_cmd_buf.bindDepthStencilState(depth_stencil_state);
|
||||||
|
|
||||||
|
/* Configure for shape drawing. */
|
||||||
|
m_dyn_cmd_buf.bindColorWriteState(dk::ColorWriteState{}.setMask(0, 0));
|
||||||
|
this->SetUniforms(ctx, call.uniformOffset, 0);
|
||||||
|
m_dyn_cmd_buf.bindRasterizerState(dk::RasterizerState{}.setCullMode(DkFace_None));
|
||||||
|
|
||||||
|
/* Draw vertices. */
|
||||||
|
for (int i = 0; i < npaths; i++) {
|
||||||
|
m_dyn_cmd_buf.draw(DkPrimitive_TriangleFan, paths[i].fillCount, 1, paths[i].fillOffset, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dyn_cmd_buf.bindColorWriteState(dk::ColorWriteState{});
|
||||||
|
this->SetUniforms(ctx, call.uniformOffset + ctx.fragSize, call.image);
|
||||||
|
m_dyn_cmd_buf.bindRasterizerState(dk::RasterizerState{});
|
||||||
|
|
||||||
|
if (ctx.flags & NVG_ANTIALIAS) {
|
||||||
|
/* Configure stencil anti-aliasing. */
|
||||||
|
depth_stencil_state
|
||||||
|
.setStencilFrontCompareOp(DkCompareOp_Equal)
|
||||||
|
.setStencilFrontFailOp(DkStencilOp_Keep)
|
||||||
|
.setStencilFrontDepthFailOp(DkStencilOp_Keep)
|
||||||
|
.setStencilFrontPassOp(DkStencilOp_Keep)
|
||||||
|
.setStencilBackCompareOp(DkCompareOp_Equal)
|
||||||
|
.setStencilBackFailOp(DkStencilOp_Keep)
|
||||||
|
.setStencilBackDepthFailOp(DkStencilOp_Keep)
|
||||||
|
.setStencilBackPassOp(DkStencilOp_Keep);
|
||||||
|
m_dyn_cmd_buf.bindDepthStencilState(depth_stencil_state);
|
||||||
|
|
||||||
|
/* Draw fringes. */
|
||||||
|
for (int i = 0; i < npaths; i++) {
|
||||||
|
m_dyn_cmd_buf.draw(DkPrimitive_TriangleStrip, paths[i].strokeCount, 1, paths[i].strokeOffset, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure and draw fill. */
|
||||||
|
depth_stencil_state
|
||||||
|
.setStencilFrontCompareOp(DkCompareOp_NotEqual)
|
||||||
|
.setStencilFrontFailOp(DkStencilOp_Zero)
|
||||||
|
.setStencilFrontDepthFailOp(DkStencilOp_Zero)
|
||||||
|
.setStencilFrontPassOp(DkStencilOp_Zero)
|
||||||
|
.setStencilBackCompareOp(DkCompareOp_NotEqual)
|
||||||
|
.setStencilBackFailOp(DkStencilOp_Zero)
|
||||||
|
.setStencilBackDepthFailOp(DkStencilOp_Zero)
|
||||||
|
.setStencilBackPassOp(DkStencilOp_Zero);
|
||||||
|
m_dyn_cmd_buf.bindDepthStencilState(depth_stencil_state);
|
||||||
|
|
||||||
|
m_dyn_cmd_buf.draw(DkPrimitive_TriangleStrip, call.triangleCount, 1, call.triangleOffset, 0);
|
||||||
|
|
||||||
|
/* Reset the depth stencil state to default. */
|
||||||
|
m_dyn_cmd_buf.bindDepthStencilState(dk::DepthStencilState{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void DkRenderer::DrawConvexFill(const DKNVGcontext &ctx, const DKNVGcall &call) {
|
||||||
|
DKNVGpath *paths = &ctx.paths[call.pathOffset];
|
||||||
|
int npaths = call.pathCount;
|
||||||
|
|
||||||
|
this->SetUniforms(ctx, call.uniformOffset, call.image);
|
||||||
|
|
||||||
|
for (int i = 0; i < npaths; i++) {
|
||||||
|
m_dyn_cmd_buf.draw(DkPrimitive_TriangleFan, paths[i].fillCount, 1, paths[i].fillOffset, 0);
|
||||||
|
|
||||||
|
/* Draw fringes. */
|
||||||
|
if (paths[i].strokeCount > 0) {
|
||||||
|
m_dyn_cmd_buf.draw(DkPrimitive_TriangleStrip, paths[i].strokeCount, 1, paths[i].strokeOffset, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DkRenderer::DrawStroke(const DKNVGcontext &ctx, const DKNVGcall &call) {
|
||||||
|
DKNVGpath* paths = &ctx.paths[call.pathOffset];
|
||||||
|
int npaths = call.pathCount;
|
||||||
|
|
||||||
|
if (ctx.flags & NVG_STENCIL_STROKES) {
|
||||||
|
/* Set the stencil to be used. */
|
||||||
|
m_dyn_cmd_buf.setStencil(DkFace_Front, 0xFF, 0x0, 0xFF);
|
||||||
|
|
||||||
|
/* Configure for filling the stroke base without overlap. */
|
||||||
|
auto depth_stencil_state = dk::DepthStencilState{}
|
||||||
|
.setStencilTestEnable(true)
|
||||||
|
.setStencilFrontCompareOp(DkCompareOp_Equal)
|
||||||
|
.setStencilFrontFailOp(DkStencilOp_Keep)
|
||||||
|
.setStencilFrontDepthFailOp(DkStencilOp_Keep)
|
||||||
|
.setStencilFrontPassOp(DkStencilOp_Incr);
|
||||||
|
m_dyn_cmd_buf.bindDepthStencilState(depth_stencil_state);
|
||||||
|
this->SetUniforms(ctx, call.uniformOffset + ctx.fragSize, call.image);
|
||||||
|
|
||||||
|
/* Draw vertices. */
|
||||||
|
for (int i = 0; i < npaths; i++) {
|
||||||
|
m_dyn_cmd_buf.draw(DkPrimitive_TriangleStrip, paths[i].strokeCount, 1, paths[i].strokeOffset, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure for drawing anti-aliased pixels. */
|
||||||
|
depth_stencil_state.setStencilFrontPassOp(DkStencilOp_Keep);
|
||||||
|
m_dyn_cmd_buf.bindDepthStencilState(depth_stencil_state);
|
||||||
|
this->SetUniforms(ctx, call.uniformOffset, call.image);
|
||||||
|
|
||||||
|
/* Draw vertices. */
|
||||||
|
for (int i = 0; i < npaths; i++) {
|
||||||
|
m_dyn_cmd_buf.draw(DkPrimitive_TriangleStrip, paths[i].strokeCount, 1, paths[i].strokeOffset, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure for clearing the stencil buffer. */
|
||||||
|
depth_stencil_state
|
||||||
|
.setStencilTestEnable(true)
|
||||||
|
.setStencilFrontCompareOp(DkCompareOp_Always)
|
||||||
|
.setStencilFrontFailOp(DkStencilOp_Zero)
|
||||||
|
.setStencilFrontDepthFailOp(DkStencilOp_Zero)
|
||||||
|
.setStencilFrontPassOp(DkStencilOp_Zero);
|
||||||
|
m_dyn_cmd_buf.bindDepthStencilState(depth_stencil_state);
|
||||||
|
|
||||||
|
/* Draw vertices. */
|
||||||
|
for (int i = 0; i < npaths; i++) {
|
||||||
|
m_dyn_cmd_buf.draw(DkPrimitive_TriangleStrip, paths[i].strokeCount, 1, paths[i].strokeOffset, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset the depth stencil state to default. */
|
||||||
|
m_dyn_cmd_buf.bindDepthStencilState(dk::DepthStencilState{});
|
||||||
|
} else {
|
||||||
|
this->SetUniforms(ctx, call.uniformOffset, call.image);
|
||||||
|
|
||||||
|
/* Draw vertices. */
|
||||||
|
for (int i = 0; i < npaths; i++) {
|
||||||
|
m_dyn_cmd_buf.draw(DkPrimitive_TriangleStrip, paths[i].strokeCount, 1, paths[i].strokeOffset, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DkRenderer::DrawTriangles(const DKNVGcontext &ctx, const DKNVGcall &call) {
|
||||||
|
this->SetUniforms(ctx, call.uniformOffset, call.image);
|
||||||
|
m_dyn_cmd_buf.draw(DkPrimitive_Triangles, call.triangleCount, 1, call.triangleOffset, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int DkRenderer::Create(DKNVGcontext &ctx) {
|
||||||
|
m_vertex_shader.load(m_code_mem_pool, "romfs:/shaders/fill_vsh.dksh");
|
||||||
|
|
||||||
|
/* Load the appropriate fragment shader depending on whether AA is enabled. */
|
||||||
|
if (ctx.flags & NVG_ANTIALIAS) {
|
||||||
|
m_fragment_shader.load(m_code_mem_pool, "romfs:/shaders/fill_aa_fsh.dksh");
|
||||||
|
} else {
|
||||||
|
m_fragment_shader.load(m_code_mem_pool, "romfs:/shaders/fill_fsh.dksh");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the size of fragment uniforms. */
|
||||||
|
ctx.fragSize = FragmentUniformSize;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Texture> DkRenderer::FindTexture(int id) {
|
||||||
|
for (auto it = m_textures.begin(); it != m_textures.end(); it++) {
|
||||||
|
if ((*it)->GetId() == id) {
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DkRenderer::CreateTexture(const DKNVGcontext &ctx, int type, int w, int h, int image_flags, const unsigned char* data) {
|
||||||
|
const auto texture_id = m_next_texture_id++;
|
||||||
|
auto texture = std::make_shared<Texture>(texture_id);
|
||||||
|
texture->Initialize(m_image_mem_pool, m_data_mem_pool, m_device, m_queue, type, w, h, image_flags, data);
|
||||||
|
m_textures.push_back(texture);
|
||||||
|
return texture->GetId();
|
||||||
|
}
|
||||||
|
|
||||||
|
int DkRenderer::DeleteTexture(const DKNVGcontext &ctx, int image) {
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
for (auto it = m_textures.begin(); it != m_textures.end();) {
|
||||||
|
/* Remove textures with the given id. */
|
||||||
|
if ((*it)->GetId() == image) {
|
||||||
|
it = m_textures.erase(it);
|
||||||
|
found = true;
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free any used image descriptors. */
|
||||||
|
this->FreeImageDescriptor(image);
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DkRenderer::UpdateTexture(const DKNVGcontext &ctx, int image, int x, int y, int w, int h, const unsigned char *data) {
|
||||||
|
const std::shared_ptr<Texture> texture = this->FindTexture(image);
|
||||||
|
|
||||||
|
/* Could not find a texture. */
|
||||||
|
if (texture == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DKNVGtextureDescriptor &tex_desc = texture->GetDescriptor();
|
||||||
|
if (tex_desc.type == NVG_TEXTURE_RGBA) {
|
||||||
|
data += y * tex_desc.width*4;
|
||||||
|
} else {
|
||||||
|
data += y * tex_desc.width;
|
||||||
|
}
|
||||||
|
x = 0;
|
||||||
|
w = tex_desc.width;
|
||||||
|
|
||||||
|
UpdateImage(texture->GetImage(), m_data_mem_pool, m_device, m_queue, tex_desc.type, x, y, w, h, data);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DkRenderer::GetTextureSize(const DKNVGcontext &ctx, int image, int *w, int *h) {
|
||||||
|
const auto descriptor = this->GetTextureDescriptor(ctx, image);
|
||||||
|
if (descriptor == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*w = descriptor->width;
|
||||||
|
*h = descriptor->height;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DKNVGtextureDescriptor *DkRenderer::GetTextureDescriptor(const DKNVGcontext &ctx, int id) {
|
||||||
|
for (auto it = m_textures.begin(); it != m_textures.end(); it++) {
|
||||||
|
if ((*it)->GetId() == id) {
|
||||||
|
return &(*it)->GetDescriptor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DkRenderer::Flush(DKNVGcontext &ctx) {
|
||||||
|
if (ctx.ncalls > 0) {
|
||||||
|
/* Prepare dynamic command buffer. */
|
||||||
|
m_dyn_cmd_mem.begin(m_dyn_cmd_buf);
|
||||||
|
|
||||||
|
/* Update buffers with data. */
|
||||||
|
this->UpdateVertexBuffer(ctx.verts, ctx.nverts * sizeof(NVGvertex));
|
||||||
|
|
||||||
|
/* Enable blending. */
|
||||||
|
m_dyn_cmd_buf.bindColorState(dk::ColorState{}.setBlendEnable(0, true));
|
||||||
|
|
||||||
|
/* Setup. */
|
||||||
|
m_dyn_cmd_buf.bindShaders(DkStageFlag_GraphicsMask, { m_vertex_shader, m_fragment_shader });
|
||||||
|
m_dyn_cmd_buf.bindVtxAttribState(VertexAttribState);
|
||||||
|
m_dyn_cmd_buf.bindVtxBufferState(VertexBufferState);
|
||||||
|
m_dyn_cmd_buf.bindVtxBuffer(0, m_vertex_buffer->getGpuAddr(), m_vertex_buffer->getSize());
|
||||||
|
|
||||||
|
/* Push the view size to the uniform buffer and bind it. */
|
||||||
|
const auto view = View{glm::vec2{m_view_width, m_view_height}};
|
||||||
|
m_dyn_cmd_buf.pushConstants(m_view_uniform_buffer.getGpuAddr(), m_view_uniform_buffer.getSize(), 0, sizeof(view), &view);
|
||||||
|
m_dyn_cmd_buf.bindUniformBuffer(DkStage_Vertex, 0, m_view_uniform_buffer.getGpuAddr(), m_view_uniform_buffer.getSize());
|
||||||
|
|
||||||
|
/* Iterate over calls. */
|
||||||
|
for (int i = 0; i < ctx.ncalls; i++) {
|
||||||
|
const DKNVGcall &call = ctx.calls[i];
|
||||||
|
|
||||||
|
/* Perform blending. */
|
||||||
|
m_dyn_cmd_buf.bindBlendStates(0, { dk::BlendState{}.setFactors(static_cast<DkBlendFactor>(call.blendFunc.srcRGB), static_cast<DkBlendFactor>(call.blendFunc.dstRGB), static_cast<DkBlendFactor>(call.blendFunc.srcAlpha), static_cast<DkBlendFactor>(call.blendFunc.dstRGB)) });
|
||||||
|
|
||||||
|
if (call.type == DKNVG_FILL) {
|
||||||
|
this->DrawFill(ctx, call);
|
||||||
|
} else if (call.type == DKNVG_CONVEXFILL) {
|
||||||
|
this->DrawConvexFill(ctx, call);
|
||||||
|
} else if (call.type == DKNVG_STROKE) {
|
||||||
|
this->DrawStroke(ctx, call);
|
||||||
|
} else if (call.type == DKNVG_TRIANGLES) {
|
||||||
|
this->DrawTriangles(ctx, call);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_queue.submitCommands(m_dyn_cmd_mem.end(m_dyn_cmd_buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset calls. */
|
||||||
|
ctx.nverts = 0;
|
||||||
|
ctx.npaths = 0;
|
||||||
|
ctx.ncalls = 0;
|
||||||
|
ctx.nuniforms = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CApplication.cpp: Wrapper class containing common application boilerplate
|
||||||
|
*/
|
||||||
|
#include "CApplication.h"
|
||||||
|
|
||||||
|
CApplication::CApplication()
|
||||||
|
{
|
||||||
|
appletLockExit();
|
||||||
|
appletSetFocusHandlingMode(AppletFocusHandlingMode_NoSuspend);
|
||||||
|
}
|
||||||
|
|
||||||
|
CApplication::~CApplication()
|
||||||
|
{
|
||||||
|
appletSetFocusHandlingMode(AppletFocusHandlingMode_SuspendHomeSleep);
|
||||||
|
appletUnlockExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CApplication::run()
|
||||||
|
{
|
||||||
|
u64 tick_ref = armGetSystemTick();
|
||||||
|
u64 tick_saved = tick_ref;
|
||||||
|
bool focused = appletGetFocusState() == AppletFocusState_Focused;
|
||||||
|
|
||||||
|
onOperationMode(appletGetOperationMode());
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
u32 msg = 0;
|
||||||
|
Result rc = appletGetMessage(&msg);
|
||||||
|
if (R_SUCCEEDED(rc))
|
||||||
|
{
|
||||||
|
bool should_close = !appletProcessMessage(msg);
|
||||||
|
if (should_close)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (msg)
|
||||||
|
{
|
||||||
|
case AppletMessage_FocusStateChanged:
|
||||||
|
{
|
||||||
|
bool old_focused = focused;
|
||||||
|
AppletFocusState state = appletGetFocusState();
|
||||||
|
focused = state == AppletFocusState_Focused;
|
||||||
|
|
||||||
|
onFocusState(state);
|
||||||
|
if (focused == old_focused)
|
||||||
|
break;
|
||||||
|
if (focused)
|
||||||
|
{
|
||||||
|
appletSetFocusHandlingMode(AppletFocusHandlingMode_NoSuspend);
|
||||||
|
tick_ref += armGetSystemTick() - tick_saved;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tick_saved = armGetSystemTick();
|
||||||
|
appletSetFocusHandlingMode(AppletFocusHandlingMode_SuspendHomeSleepNotify);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AppletMessage_OperationModeChanged:
|
||||||
|
onOperationMode(appletGetOperationMode());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (focused && !onFrame(armTicksToNs(armGetSystemTick() - tick_ref)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CExternalImage.cpp: Utility class for loading images from the filesystem
|
||||||
|
*/
|
||||||
|
#include "CExternalImage.h"
|
||||||
|
#include "FileLoader.h"
|
||||||
|
|
||||||
|
bool CExternalImage::load(CMemPool& imagePool, CMemPool& scratchPool, dk::Device device, dk::Queue transferQueue, const char* path, uint32_t width, uint32_t height, DkImageFormat format, uint32_t flags)
|
||||||
|
{
|
||||||
|
CMemPool::Handle tempimgmem = LoadFile(scratchPool, path, DK_IMAGE_LINEAR_STRIDE_ALIGNMENT);
|
||||||
|
if (!tempimgmem)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
dk::UniqueCmdBuf tempcmdbuf = dk::CmdBufMaker{device}.create();
|
||||||
|
CMemPool::Handle tempcmdmem = scratchPool.allocate(DK_MEMBLOCK_ALIGNMENT);
|
||||||
|
tempcmdbuf.addMemory(tempcmdmem.getMemBlock(), tempcmdmem.getOffset(), tempcmdmem.getSize());
|
||||||
|
|
||||||
|
dk::ImageLayout layout;
|
||||||
|
dk::ImageLayoutMaker{device}
|
||||||
|
.setFlags(flags)
|
||||||
|
.setFormat(format)
|
||||||
|
.setDimensions(width, height)
|
||||||
|
.initialize(layout);
|
||||||
|
|
||||||
|
m_mem = imagePool.allocate(layout.getSize(), layout.getAlignment());
|
||||||
|
m_image.initialize(layout, m_mem.getMemBlock(), m_mem.getOffset());
|
||||||
|
m_descriptor.initialize(m_image);
|
||||||
|
|
||||||
|
dk::ImageView imageView{m_image};
|
||||||
|
tempcmdbuf.copyBufferToImage({ tempimgmem.getGpuAddr() }, imageView, { 0, 0, 0, width, height, 1 });
|
||||||
|
transferQueue.submitCommands(tempcmdbuf.finishList());
|
||||||
|
transferQueue.waitIdle();
|
||||||
|
|
||||||
|
tempcmdmem.destroy();
|
||||||
|
tempimgmem.destroy();
|
||||||
|
return true;
|
||||||
|
}
|
214
troposphere/daybreak/nanovg/source/framework/CIntrusiveTree.cpp
Normal file
214
troposphere/daybreak/nanovg/source/framework/CIntrusiveTree.cpp
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CIntrusiveTree.cpp: Intrusive red-black tree helper class
|
||||||
|
*/
|
||||||
|
#include "CIntrusiveTree.h"
|
||||||
|
|
||||||
|
// This red-black tree implementation is mostly based on mtheall's work,
|
||||||
|
// which can be found here:
|
||||||
|
// https://github.com/smealum/ctrulib/tree/master/libctru/source/util/rbtree
|
||||||
|
|
||||||
|
void CIntrusiveTreeBase::rotate(N* node, N::Leaf leaf)
|
||||||
|
{
|
||||||
|
N *tmp = node->child(leaf);
|
||||||
|
N *parent = node->getParent();
|
||||||
|
|
||||||
|
node->child(leaf) = tmp->child(!leaf);
|
||||||
|
if (tmp->child(!leaf))
|
||||||
|
tmp->child(!leaf)->setParent(node);
|
||||||
|
|
||||||
|
tmp->child(!leaf) = node;
|
||||||
|
tmp->setParent(parent);
|
||||||
|
|
||||||
|
if (parent)
|
||||||
|
{
|
||||||
|
if (node == parent->child(!leaf))
|
||||||
|
parent->child(!leaf) = tmp;
|
||||||
|
else
|
||||||
|
parent->child(leaf) = tmp;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_root = tmp;
|
||||||
|
|
||||||
|
node->setParent(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CIntrusiveTreeBase::recolor(N* parent, N* node)
|
||||||
|
{
|
||||||
|
N *sibling;
|
||||||
|
|
||||||
|
while ((!node || node->isBlack()) && node != m_root)
|
||||||
|
{
|
||||||
|
N::Leaf leaf = node == parent->left() ? N::Right : N::Left;
|
||||||
|
sibling = parent->child(leaf);
|
||||||
|
|
||||||
|
if (sibling->isRed())
|
||||||
|
{
|
||||||
|
sibling->setBlack();
|
||||||
|
parent->setRed();
|
||||||
|
rotate(parent, leaf);
|
||||||
|
sibling = parent->child(leaf);
|
||||||
|
}
|
||||||
|
|
||||||
|
N::Color clr[2];
|
||||||
|
clr[N::Left] = sibling->left() ? sibling->left()->getColor() : N::Black;
|
||||||
|
clr[N::Right] = sibling->right() ? sibling->right()->getColor() : N::Black;
|
||||||
|
|
||||||
|
if (clr[N::Left] == N::Black && clr[N::Right] == N::Black)
|
||||||
|
{
|
||||||
|
sibling->setRed();
|
||||||
|
node = parent;
|
||||||
|
parent = node->getParent();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (clr[leaf] == N::Black)
|
||||||
|
{
|
||||||
|
sibling->child(!leaf)->setBlack();
|
||||||
|
sibling->setRed();
|
||||||
|
rotate(sibling, !leaf);
|
||||||
|
sibling = parent->child(leaf);
|
||||||
|
}
|
||||||
|
|
||||||
|
sibling->setColor(parent->getColor());
|
||||||
|
parent->setBlack();
|
||||||
|
sibling->child(leaf)->setBlack();
|
||||||
|
rotate(parent, leaf);
|
||||||
|
|
||||||
|
node = m_root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node)
|
||||||
|
node->setBlack();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CIntrusiveTreeBase::walk(N* node, N::Leaf leaf) const -> N*
|
||||||
|
{
|
||||||
|
if (node->child(leaf))
|
||||||
|
{
|
||||||
|
node = node->child(leaf);
|
||||||
|
while (node->child(!leaf))
|
||||||
|
node = node->child(!leaf);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
N *parent = node->getParent();
|
||||||
|
while (parent && node == parent->child(leaf))
|
||||||
|
{
|
||||||
|
node = parent;
|
||||||
|
parent = node->getParent();
|
||||||
|
}
|
||||||
|
node = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CIntrusiveTreeBase::insert(N* node, N* parent)
|
||||||
|
{
|
||||||
|
node->left() = node->right() = nullptr;
|
||||||
|
node->setParent(parent);
|
||||||
|
node->setRed();
|
||||||
|
|
||||||
|
while ((parent = node->getParent()) && parent->isRed())
|
||||||
|
{
|
||||||
|
N *grandparent = parent->getParent();
|
||||||
|
N::Leaf leaf = parent == grandparent->left() ? N::Right : N::Left;
|
||||||
|
N *uncle = grandparent->child(leaf);
|
||||||
|
|
||||||
|
if (uncle && uncle->isRed())
|
||||||
|
{
|
||||||
|
uncle->setBlack();
|
||||||
|
parent->setBlack();
|
||||||
|
grandparent->setRed();
|
||||||
|
|
||||||
|
node = grandparent;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (parent->child(leaf) == node)
|
||||||
|
{
|
||||||
|
rotate(parent, leaf);
|
||||||
|
|
||||||
|
N* tmp = parent;
|
||||||
|
parent = node;
|
||||||
|
node = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent->setBlack();
|
||||||
|
grandparent->setRed();
|
||||||
|
rotate(grandparent, !leaf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_root->setBlack();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CIntrusiveTreeBase::remove(N* node)
|
||||||
|
{
|
||||||
|
N::Color color;
|
||||||
|
N *child, *parent;
|
||||||
|
|
||||||
|
if (node->left() && node->right())
|
||||||
|
{
|
||||||
|
N *old = node;
|
||||||
|
|
||||||
|
node = node->right();
|
||||||
|
while (node->left())
|
||||||
|
node = node->left();
|
||||||
|
|
||||||
|
parent = old->getParent();
|
||||||
|
if (parent)
|
||||||
|
{
|
||||||
|
if (parent->left() == old)
|
||||||
|
parent->left() = node;
|
||||||
|
else
|
||||||
|
parent->right() = node;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_root = node;
|
||||||
|
|
||||||
|
child = node->right();
|
||||||
|
parent = node->getParent();
|
||||||
|
color = node->getColor();
|
||||||
|
|
||||||
|
if (parent == old)
|
||||||
|
parent = node;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (child)
|
||||||
|
child->setParent(parent);
|
||||||
|
parent->left() = child;
|
||||||
|
|
||||||
|
node->right() = old->right();
|
||||||
|
old->right()->setParent(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
node->setParent(old->getParent());
|
||||||
|
node->setColor(old->getColor());
|
||||||
|
node->left() = old->left();
|
||||||
|
old->left()->setParent(node);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
child = node->left() ? node->right() : node->left();
|
||||||
|
parent = node->getParent();
|
||||||
|
color = node->getColor();
|
||||||
|
|
||||||
|
if (child)
|
||||||
|
child->setParent(parent);
|
||||||
|
if (parent)
|
||||||
|
{
|
||||||
|
if (parent->left() == node)
|
||||||
|
parent->left() = child;
|
||||||
|
else
|
||||||
|
parent->right() = child;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_root = child;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color == N::Black)
|
||||||
|
recolor(parent, child);
|
||||||
|
}
|
175
troposphere/daybreak/nanovg/source/framework/CMemPool.cpp
Normal file
175
troposphere/daybreak/nanovg/source/framework/CMemPool.cpp
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CMemPool.cpp: Pooled dynamic memory allocation manager class
|
||||||
|
*/
|
||||||
|
#include "CMemPool.h"
|
||||||
|
|
||||||
|
inline auto CMemPool::_newSlice() -> Slice*
|
||||||
|
{
|
||||||
|
Slice* ret = m_sliceHeap.pop();
|
||||||
|
if (!ret) ret = (Slice*)::malloc(sizeof(Slice));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void CMemPool::_deleteSlice(Slice* s)
|
||||||
|
{
|
||||||
|
if (!s) return;
|
||||||
|
m_sliceHeap.add(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
CMemPool::~CMemPool()
|
||||||
|
{
|
||||||
|
m_memMap.iterate([](Slice* s) { ::free(s); });
|
||||||
|
m_sliceHeap.iterate([](Slice* s) { ::free(s); });
|
||||||
|
m_blocks.iterate([](Block* blk) {
|
||||||
|
blk->m_obj.destroy();
|
||||||
|
::free(blk);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CMemPool::allocate(uint32_t size, uint32_t alignment) -> Handle
|
||||||
|
{
|
||||||
|
if (!size) return nullptr;
|
||||||
|
if (alignment & (alignment - 1)) return nullptr;
|
||||||
|
size = (size + alignment - 1) &~ (alignment - 1);
|
||||||
|
#ifdef DEBUG_CMEMPOOL
|
||||||
|
printf("Allocating size=%u alignment=0x%x\n", size, alignment);
|
||||||
|
{
|
||||||
|
Slice* temp = /*m_freeList*/m_memMap.first();
|
||||||
|
while (temp)
|
||||||
|
{
|
||||||
|
printf("-- blk %p | 0x%08x-0x%08x | %s used\n", temp->m_block, temp->m_start, temp->m_end, temp->m_pool ? " " : "not");
|
||||||
|
temp = /*m_freeList*/m_memMap.next(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint32_t start_offset = 0;
|
||||||
|
uint32_t end_offset = 0;
|
||||||
|
Slice* slice = m_freeList.find(size, decltype(m_freeList)::LowerBound);
|
||||||
|
while (slice)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_CMEMPOOL
|
||||||
|
printf(" * Checking slice 0x%x - 0x%x\n", slice->m_start, slice->m_end);
|
||||||
|
#endif
|
||||||
|
start_offset = (slice->m_start + alignment - 1) &~ (alignment - 1);
|
||||||
|
end_offset = start_offset + size;
|
||||||
|
if (end_offset <= slice->m_end)
|
||||||
|
break;
|
||||||
|
slice = m_freeList.next(slice);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!slice)
|
||||||
|
{
|
||||||
|
Block* blk = (Block*)::malloc(sizeof(Block));
|
||||||
|
if (!blk)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
uint32_t unusableSize = (m_flags & DkMemBlockFlags_Code) ? DK_SHADER_CODE_UNUSABLE_SIZE : 0;
|
||||||
|
uint32_t blkSize = m_blockSize - unusableSize;
|
||||||
|
blkSize = size > blkSize ? size : blkSize;
|
||||||
|
blkSize = (blkSize + unusableSize + DK_MEMBLOCK_ALIGNMENT - 1) &~ (DK_MEMBLOCK_ALIGNMENT - 1);
|
||||||
|
#ifdef DEBUG_CMEMPOOL
|
||||||
|
printf(" ! Allocating block of size 0x%x\n", blkSize);
|
||||||
|
#endif
|
||||||
|
blk->m_obj = dk::MemBlockMaker{m_dev, blkSize}.setFlags(m_flags).create();
|
||||||
|
if (!blk->m_obj)
|
||||||
|
{
|
||||||
|
::free(blk);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
slice = _newSlice();
|
||||||
|
if (!slice)
|
||||||
|
{
|
||||||
|
blk->m_obj.destroy();
|
||||||
|
::free(blk);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
slice->m_pool = nullptr;
|
||||||
|
slice->m_block = blk;
|
||||||
|
slice->m_start = 0;
|
||||||
|
slice->m_end = blkSize - unusableSize;
|
||||||
|
m_memMap.add(slice);
|
||||||
|
|
||||||
|
blk->m_cpuAddr = blk->m_obj.getCpuAddr();
|
||||||
|
blk->m_gpuAddr = blk->m_obj.getGpuAddr();
|
||||||
|
m_blocks.add(blk);
|
||||||
|
|
||||||
|
start_offset = 0;
|
||||||
|
end_offset = size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_CMEMPOOL
|
||||||
|
printf(" * found it\n");
|
||||||
|
#endif
|
||||||
|
m_freeList.remove(slice);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start_offset != slice->m_start)
|
||||||
|
{
|
||||||
|
Slice* t = _newSlice();
|
||||||
|
if (!t) goto _bad;
|
||||||
|
t->m_pool = nullptr;
|
||||||
|
t->m_block = slice->m_block;
|
||||||
|
t->m_start = slice->m_start;
|
||||||
|
t->m_end = start_offset;
|
||||||
|
#ifdef DEBUG_CMEMPOOL
|
||||||
|
printf("-> subdivide left: %08x-%08x\n", t->m_start, t->m_end);
|
||||||
|
#endif
|
||||||
|
m_memMap.addBefore(slice, t);
|
||||||
|
m_freeList.insert(t, true);
|
||||||
|
slice->m_start = start_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end_offset != slice->m_end)
|
||||||
|
{
|
||||||
|
Slice* t = _newSlice();
|
||||||
|
if (!t) goto _bad;
|
||||||
|
t->m_pool = nullptr;
|
||||||
|
t->m_block = slice->m_block;
|
||||||
|
t->m_start = end_offset;
|
||||||
|
t->m_end = slice->m_end;
|
||||||
|
#ifdef DEBUG_CMEMPOOL
|
||||||
|
printf("-> subdivide right: %08x-%08x\n", t->m_start, t->m_end);
|
||||||
|
#endif
|
||||||
|
m_memMap.addAfter(slice, t);
|
||||||
|
m_freeList.insert(t, true);
|
||||||
|
slice->m_end = end_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
slice->m_pool = this;
|
||||||
|
return slice;
|
||||||
|
|
||||||
|
_bad:
|
||||||
|
m_freeList.insert(slice, true);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMemPool::_destroy(Slice* slice)
|
||||||
|
{
|
||||||
|
slice->m_pool = nullptr;
|
||||||
|
|
||||||
|
Slice* left = m_memMap.prev(slice);
|
||||||
|
Slice* right = m_memMap.next(slice);
|
||||||
|
|
||||||
|
if (left && left->canCoalesce(*slice))
|
||||||
|
{
|
||||||
|
slice->m_start = left->m_start;
|
||||||
|
m_freeList.remove(left);
|
||||||
|
m_memMap.remove(left);
|
||||||
|
_deleteSlice(left);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (right && slice->canCoalesce(*right))
|
||||||
|
{
|
||||||
|
slice->m_end = right->m_end;
|
||||||
|
m_freeList.remove(right);
|
||||||
|
m_memMap.remove(right);
|
||||||
|
_deleteSlice(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_freeList.insert(slice, true);
|
||||||
|
}
|
62
troposphere/daybreak/nanovg/source/framework/CShader.cpp
Normal file
62
troposphere/daybreak/nanovg/source/framework/CShader.cpp
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** CShader.cpp: Utility class for loading shaders from the filesystem
|
||||||
|
*/
|
||||||
|
#include "CShader.h"
|
||||||
|
|
||||||
|
struct DkshHeader
|
||||||
|
{
|
||||||
|
uint32_t magic; // DKSH_MAGIC
|
||||||
|
uint32_t header_sz; // sizeof(DkshHeader)
|
||||||
|
uint32_t control_sz;
|
||||||
|
uint32_t code_sz;
|
||||||
|
uint32_t programs_off;
|
||||||
|
uint32_t num_programs;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool CShader::load(CMemPool& pool, const char* path)
|
||||||
|
{
|
||||||
|
FILE* f;
|
||||||
|
DkshHeader hdr;
|
||||||
|
void* ctrlmem;
|
||||||
|
|
||||||
|
m_codemem.destroy();
|
||||||
|
|
||||||
|
f = fopen(path, "rb");
|
||||||
|
if (!f) return false;
|
||||||
|
|
||||||
|
if (!fread(&hdr, sizeof(hdr), 1, f))
|
||||||
|
goto _fail0;
|
||||||
|
|
||||||
|
ctrlmem = malloc(hdr.control_sz);
|
||||||
|
if (!ctrlmem)
|
||||||
|
goto _fail0;
|
||||||
|
|
||||||
|
rewind(f);
|
||||||
|
if (!fread(ctrlmem, hdr.control_sz, 1, f))
|
||||||
|
goto _fail1;
|
||||||
|
|
||||||
|
m_codemem = pool.allocate(hdr.code_sz, DK_SHADER_CODE_ALIGNMENT);
|
||||||
|
if (!m_codemem)
|
||||||
|
goto _fail1;
|
||||||
|
|
||||||
|
if (!fread(m_codemem.getCpuAddr(), hdr.code_sz, 1, f))
|
||||||
|
goto _fail2;
|
||||||
|
|
||||||
|
dk::ShaderMaker{m_codemem.getMemBlock(), m_codemem.getOffset()}
|
||||||
|
.setControl(ctrlmem)
|
||||||
|
.setProgramId(0)
|
||||||
|
.initialize(m_shader);
|
||||||
|
|
||||||
|
free(ctrlmem);
|
||||||
|
fclose(f);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
_fail2:
|
||||||
|
m_codemem.destroy();
|
||||||
|
_fail1:
|
||||||
|
free(ctrlmem);
|
||||||
|
_fail0:
|
||||||
|
fclose(f);
|
||||||
|
return false;
|
||||||
|
}
|
27
troposphere/daybreak/nanovg/source/framework/FileLoader.cpp
Normal file
27
troposphere/daybreak/nanovg/source/framework/FileLoader.cpp
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
** Sample Framework for deko3d Applications
|
||||||
|
** FileLoader.cpp: Helpers for loading data from the filesystem directly into GPU memory
|
||||||
|
*/
|
||||||
|
#include "FileLoader.h"
|
||||||
|
|
||||||
|
CMemPool::Handle LoadFile(CMemPool& pool, const char* path, uint32_t alignment)
|
||||||
|
{
|
||||||
|
FILE *f = fopen(path, "rb");
|
||||||
|
if (!f) return nullptr;
|
||||||
|
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
uint32_t fsize = ftell(f);
|
||||||
|
rewind(f);
|
||||||
|
|
||||||
|
CMemPool::Handle mem = pool.allocate(fsize, alignment);
|
||||||
|
if (!mem)
|
||||||
|
{
|
||||||
|
fclose(f);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
fread(mem.getCpuAddr(), fsize, 1, f);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
return mem;
|
||||||
|
}
|
18
troposphere/daybreak/nanovg/source/framework/LICENSE
Normal file
18
troposphere/daybreak/nanovg/source/framework/LICENSE
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
Copyright (C) 2020 fincs
|
||||||
|
|
||||||
|
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.
|
2926
troposphere/daybreak/nanovg/source/nanovg.c
Normal file
2926
troposphere/daybreak/nanovg/source/nanovg.c
Normal file
File diff suppressed because it is too large
Load diff
158
troposphere/daybreak/source/ams_su.c
Normal file
158
troposphere/daybreak/source/ams_su.c
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <switch.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "ams_su.h"
|
||||||
|
#include "service_guard.h"
|
||||||
|
|
||||||
|
static Service g_amssuSrv;
|
||||||
|
static TransferMemory g_tmem;
|
||||||
|
|
||||||
|
NX_GENERATE_SERVICE_GUARD(amssu);
|
||||||
|
|
||||||
|
Result _amssuInitialize(void) {
|
||||||
|
return smGetService(&g_amssuSrv, "ams:su");
|
||||||
|
}
|
||||||
|
|
||||||
|
void _amssuCleanup(void) {
|
||||||
|
serviceClose(&g_amssuSrv);
|
||||||
|
tmemClose(&g_tmem);
|
||||||
|
}
|
||||||
|
|
||||||
|
Service *amssuGetServiceSession(void) {
|
||||||
|
return &g_amssuSrv;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result amssuGetUpdateInformation(AmsSuUpdateInformation *out, const char *path) {
|
||||||
|
char send_path[FS_MAX_PATH] = {0};
|
||||||
|
strncpy(send_path, path, FS_MAX_PATH-1);
|
||||||
|
send_path[FS_MAX_PATH-1] = 0;
|
||||||
|
|
||||||
|
return serviceDispatchOut(&g_amssuSrv, 0, *out,
|
||||||
|
.buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcPointer | SfBufferAttr_FixedSize },
|
||||||
|
.buffers = { { send_path, FS_MAX_PATH } },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result amssuValidateUpdate(AmsSuUpdateValidationInfo *out, const char *path) {
|
||||||
|
char send_path[FS_MAX_PATH] = {0};
|
||||||
|
strncpy(send_path, path, FS_MAX_PATH-1);
|
||||||
|
send_path[FS_MAX_PATH-1] = 0;
|
||||||
|
|
||||||
|
return serviceDispatchOut(&g_amssuSrv, 1, *out,
|
||||||
|
.buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcPointer | SfBufferAttr_FixedSize },
|
||||||
|
.buffers = { { send_path, FS_MAX_PATH } },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result amssuSetupUpdate(void *buffer, size_t size, const char *path, bool exfat) {
|
||||||
|
Result rc = 0;
|
||||||
|
|
||||||
|
if (buffer == NULL) {
|
||||||
|
rc = tmemCreate(&g_tmem, size, Perm_None);
|
||||||
|
} else {
|
||||||
|
rc = tmemCreateFromMemory(&g_tmem, buffer, size, Perm_None);
|
||||||
|
}
|
||||||
|
if (R_FAILED(rc)) return rc;
|
||||||
|
|
||||||
|
char send_path[FS_MAX_PATH] = {0};
|
||||||
|
strncpy(send_path, path, FS_MAX_PATH-1);
|
||||||
|
send_path[FS_MAX_PATH-1] = 0;
|
||||||
|
|
||||||
|
const struct {
|
||||||
|
u8 exfat;
|
||||||
|
u64 size;
|
||||||
|
} in = { exfat, g_tmem.size };
|
||||||
|
|
||||||
|
rc = serviceDispatchIn(&g_amssuSrv, 2, in,
|
||||||
|
.in_num_handles = 1,
|
||||||
|
.in_handles = { g_tmem.handle },
|
||||||
|
.buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcPointer | SfBufferAttr_FixedSize },
|
||||||
|
.buffers = { { send_path, FS_MAX_PATH } },
|
||||||
|
);
|
||||||
|
if (R_FAILED((rc))) {
|
||||||
|
tmemClose(&g_tmem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result amssuSetupUpdateWithVariation(void *buffer, size_t size, const char *path, bool exfat, u32 variation) {
|
||||||
|
Result rc = 0;
|
||||||
|
|
||||||
|
if (buffer == NULL) {
|
||||||
|
rc = tmemCreate(&g_tmem, size, Perm_None);
|
||||||
|
} else {
|
||||||
|
rc = tmemCreateFromMemory(&g_tmem, buffer, size, Perm_None);
|
||||||
|
}
|
||||||
|
if (R_FAILED(rc)) return rc;
|
||||||
|
|
||||||
|
char send_path[FS_MAX_PATH] = {0};
|
||||||
|
strncpy(send_path, path, FS_MAX_PATH-1);
|
||||||
|
send_path[FS_MAX_PATH-1] = 0;
|
||||||
|
|
||||||
|
const struct {
|
||||||
|
u8 exfat;
|
||||||
|
u32 variation;
|
||||||
|
u64 size;
|
||||||
|
} in = { exfat, variation, g_tmem.size };
|
||||||
|
|
||||||
|
rc = serviceDispatchIn(&g_amssuSrv, 3, in,
|
||||||
|
.in_num_handles = 1,
|
||||||
|
.in_handles = { g_tmem.handle },
|
||||||
|
.buffer_attrs = { SfBufferAttr_In | SfBufferAttr_HipcPointer | SfBufferAttr_FixedSize },
|
||||||
|
.buffers = { { send_path, FS_MAX_PATH } },
|
||||||
|
);
|
||||||
|
if (R_FAILED((rc))) {
|
||||||
|
tmemClose(&g_tmem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result amssuRequestPrepareUpdate(AsyncResult *a) {
|
||||||
|
memset(a, 0, sizeof(*a));
|
||||||
|
|
||||||
|
Handle event = INVALID_HANDLE;
|
||||||
|
Result rc = serviceDispatch(&g_amssuSrv, 4,
|
||||||
|
.out_num_objects = 1,
|
||||||
|
.out_objects = &a->s,
|
||||||
|
.out_handle_attrs = { SfOutHandleAttr_HipcCopy },
|
||||||
|
.out_handles = &event,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (R_SUCCEEDED(rc))
|
||||||
|
eventLoadRemote(&a->event, event, false);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result amssuGetPrepareUpdateProgress(NsSystemUpdateProgress *out) {
|
||||||
|
return serviceDispatchOut(&g_amssuSrv, 5, *out);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result amssuHasPreparedUpdate(bool *out) {
|
||||||
|
u8 outval = 0;
|
||||||
|
Result rc = serviceDispatchOut(&g_amssuSrv, 6, outval);
|
||||||
|
if (R_SUCCEEDED(rc)) {
|
||||||
|
if (out) *out = outval & 1;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result amssuApplyPreparedUpdate() {
|
||||||
|
return serviceDispatch(&g_amssuSrv, 7);
|
||||||
|
}
|
51
troposphere/daybreak/source/ams_su.h
Normal file
51
troposphere/daybreak/source/ams_su.h
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u32 version;
|
||||||
|
bool exfat_supported;
|
||||||
|
u32 num_firmware_variations;
|
||||||
|
u32 firmware_variation_ids[16];
|
||||||
|
} AmsSuUpdateInformation;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Result result;
|
||||||
|
NcmContentMetaKey invalid_key;
|
||||||
|
NcmContentId invalid_content_id;
|
||||||
|
} AmsSuUpdateValidationInfo;
|
||||||
|
|
||||||
|
Result amssuInitialize();
|
||||||
|
void amssuExit();
|
||||||
|
Service *amssuGetServiceSession(void);
|
||||||
|
|
||||||
|
Result amssuGetUpdateInformation(AmsSuUpdateInformation *out, const char *path);
|
||||||
|
Result amssuValidateUpdate(AmsSuUpdateValidationInfo *out, const char *path);
|
||||||
|
Result amssuSetupUpdate(void *buffer, size_t size, const char *path, bool exfat);
|
||||||
|
Result amssuSetupUpdateWithVariation(void *buffer, size_t size, const char *path, bool exfat, u32 variation);
|
||||||
|
Result amssuRequestPrepareUpdate(AsyncResult *a);
|
||||||
|
Result amssuGetPrepareUpdateProgress(NsSystemUpdateProgress *out);
|
||||||
|
Result amssuHasPreparedUpdate(bool *out);
|
||||||
|
Result amssuApplyPreparedUpdate();
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
23
troposphere/daybreak/source/assert.hpp
Normal file
23
troposphere/daybreak/source/assert.hpp
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Adubbz
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#define DBK_ABORT_UNLESS(expr) \
|
||||||
|
if (!static_cast<bool>(expr)) { \
|
||||||
|
std::abort(); \
|
||||||
|
}
|
263
troposphere/daybreak/source/main.cpp
Normal file
263
troposphere/daybreak/source/main.cpp
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Adubbz
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <optional>
|
||||||
|
#include <switch.h>
|
||||||
|
#include <nanovg.h>
|
||||||
|
#include <nanovg_dk.h>
|
||||||
|
#include <nanovg/framework/CApplication.h>
|
||||||
|
#include "ui.hpp"
|
||||||
|
#include "ams_su.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
void userAppInit(void) {
|
||||||
|
Result rc = 0;
|
||||||
|
|
||||||
|
if (R_FAILED(rc = amssuInitialize())) {
|
||||||
|
fatalThrow(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (R_FAILED(rc = romfsInit())) {
|
||||||
|
fatalThrow(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (R_FAILED(rc = spsmInitialize())) {
|
||||||
|
fatalThrow(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (R_FAILED(rc = plInitialize(PlServiceType_User))) {
|
||||||
|
fatalThrow(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (R_FAILED(rc = hiddbgInitialize())) {
|
||||||
|
fatalThrow(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void userAppExit(void) {
|
||||||
|
hiddbgExit();
|
||||||
|
plExit();
|
||||||
|
spsmExit();
|
||||||
|
romfsExit();
|
||||||
|
amssuExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
static constexpr u32 FramebufferWidth = 1280;
|
||||||
|
static constexpr u32 FramebufferHeight = 720;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Daybreak : public CApplication {
|
||||||
|
private:
|
||||||
|
static constexpr unsigned NumFramebuffers = 2;
|
||||||
|
static constexpr unsigned StaticCmdSize = 0x1000;
|
||||||
|
|
||||||
|
dk::UniqueDevice m_device;
|
||||||
|
dk::UniqueQueue m_queue;
|
||||||
|
dk::UniqueSwapchain m_swapchain;
|
||||||
|
|
||||||
|
std::optional<CMemPool> m_pool_images;
|
||||||
|
std::optional<CMemPool> m_pool_code;
|
||||||
|
std::optional<CMemPool> m_pool_data;
|
||||||
|
|
||||||
|
dk::UniqueCmdBuf m_cmd_buf;
|
||||||
|
DkCmdList m_render_cmdlist;
|
||||||
|
|
||||||
|
dk::Image m_depth_buffer;
|
||||||
|
CMemPool::Handle m_depth_buffer_mem;
|
||||||
|
dk::Image m_framebuffers[NumFramebuffers];
|
||||||
|
CMemPool::Handle m_framebuffers_mem[NumFramebuffers];
|
||||||
|
DkCmdList m_framebuffer_cmdlists[NumFramebuffers];
|
||||||
|
|
||||||
|
std::optional<nvg::DkRenderer> m_renderer;
|
||||||
|
NVGcontext *m_vg;
|
||||||
|
int m_standard_font;
|
||||||
|
public:
|
||||||
|
Daybreak() {
|
||||||
|
Result rc = 0;
|
||||||
|
|
||||||
|
/* Create the deko3d device. */
|
||||||
|
m_device = dk::DeviceMaker{}.create();
|
||||||
|
|
||||||
|
/* Create the main queue. */
|
||||||
|
m_queue = dk::QueueMaker{m_device}.setFlags(DkQueueFlags_Graphics).create();
|
||||||
|
|
||||||
|
/* Create the memory pools. */
|
||||||
|
m_pool_images.emplace(m_device, DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image, 16*1024*1024);
|
||||||
|
m_pool_code.emplace(m_device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Code, 128*1024);
|
||||||
|
m_pool_data.emplace(m_device, DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached, 1*1024*1024);
|
||||||
|
|
||||||
|
/* Create the static command buffer and feed it freshly allocated memory. */
|
||||||
|
m_cmd_buf = dk::CmdBufMaker{m_device}.create();
|
||||||
|
CMemPool::Handle cmdmem = m_pool_data->allocate(StaticCmdSize);
|
||||||
|
m_cmd_buf.addMemory(cmdmem.getMemBlock(), cmdmem.getOffset(), cmdmem.getSize());
|
||||||
|
|
||||||
|
/* Create the framebuffer resources. */
|
||||||
|
this->CreateFramebufferResources();
|
||||||
|
|
||||||
|
m_renderer.emplace(FramebufferWidth, FramebufferHeight, m_device, m_queue, *m_pool_images, *m_pool_code, *m_pool_data);
|
||||||
|
m_vg = nvgCreateDk(&*m_renderer, NVG_ANTIALIAS | NVG_STENCIL_STROKES);
|
||||||
|
|
||||||
|
|
||||||
|
PlFontData font;
|
||||||
|
if (R_FAILED(rc = plGetSharedFontByType(&font, PlSharedFontType_Standard))) {
|
||||||
|
fatalThrow(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_standard_font = nvgCreateFontMem(m_vg, "switch-standard", static_cast<u8 *>(font.address), font.size, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
~Daybreak() {
|
||||||
|
/* Destroy the framebuffer resources. This should be done first. */
|
||||||
|
this->DestroyFramebufferResources();
|
||||||
|
|
||||||
|
/* Cleanup vg. */
|
||||||
|
nvgDeleteDk(m_vg);
|
||||||
|
|
||||||
|
/* Destroy the renderer. */
|
||||||
|
m_renderer.reset();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
void CreateFramebufferResources() {
|
||||||
|
/* Create layout for the depth buffer. */
|
||||||
|
dk::ImageLayout layout_depth_buffer;
|
||||||
|
dk::ImageLayoutMaker{m_device}
|
||||||
|
.setFlags(DkImageFlags_UsageRender | DkImageFlags_HwCompression)
|
||||||
|
.setFormat(DkImageFormat_S8)
|
||||||
|
.setDimensions(FramebufferWidth, FramebufferHeight)
|
||||||
|
.initialize(layout_depth_buffer);
|
||||||
|
|
||||||
|
/* Create the depth buffer. */
|
||||||
|
m_depth_buffer_mem = m_pool_images->allocate(layout_depth_buffer.getSize(), layout_depth_buffer.getAlignment());
|
||||||
|
m_depth_buffer.initialize(layout_depth_buffer, m_depth_buffer_mem.getMemBlock(), m_depth_buffer_mem.getOffset());
|
||||||
|
|
||||||
|
/* Create layout for the framebuffers. */
|
||||||
|
dk::ImageLayout layout_framebuffer;
|
||||||
|
dk::ImageLayoutMaker{m_device}
|
||||||
|
.setFlags(DkImageFlags_UsageRender | DkImageFlags_UsagePresent | DkImageFlags_HwCompression)
|
||||||
|
.setFormat(DkImageFormat_RGBA8_Unorm)
|
||||||
|
.setDimensions(FramebufferWidth, FramebufferHeight)
|
||||||
|
.initialize(layout_framebuffer);
|
||||||
|
|
||||||
|
/* Create the framebuffers. */
|
||||||
|
std::array<DkImage const*, NumFramebuffers> fb_array;
|
||||||
|
const u64 fb_size = layout_framebuffer.getSize();
|
||||||
|
const u32 fb_align = layout_framebuffer.getAlignment();
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < NumFramebuffers; i++) {
|
||||||
|
/* Allocate a framebuffer. */
|
||||||
|
m_framebuffers_mem[i] = m_pool_images->allocate(fb_size, fb_align);
|
||||||
|
m_framebuffers[i].initialize(layout_framebuffer, m_framebuffers_mem[i].getMemBlock(), m_framebuffers_mem[i].getOffset());
|
||||||
|
|
||||||
|
/* Generate a command list that binds it. */
|
||||||
|
dk::ImageView color_target{ m_framebuffers[i] }, depth_target{ m_depth_buffer };
|
||||||
|
m_cmd_buf.bindRenderTargets(&color_target, &depth_target);
|
||||||
|
m_framebuffer_cmdlists[i] = m_cmd_buf.finishList();
|
||||||
|
|
||||||
|
/* Fill in the array for use later by the swapchain creation code. */
|
||||||
|
fb_array[i] = &m_framebuffers[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create the swapchain using the framebuffers. */
|
||||||
|
m_swapchain = dk::SwapchainMaker{m_device, nwindowGetDefault(), fb_array}.create();
|
||||||
|
|
||||||
|
/* Generate the main rendering cmdlist. */
|
||||||
|
this->RecordStaticCommands();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DestroyFramebufferResources() {
|
||||||
|
/* Return early if we have nothing to destroy. */
|
||||||
|
if (!m_swapchain) return;
|
||||||
|
|
||||||
|
/* Make sure the queue is idle before destroying anything. */
|
||||||
|
m_queue.waitIdle();
|
||||||
|
|
||||||
|
/* Clear the static cmdbuf, destroying the static cmdlists in the process. */
|
||||||
|
m_cmd_buf.clear();
|
||||||
|
|
||||||
|
/* Destroy the swapchain. */
|
||||||
|
m_swapchain.destroy();
|
||||||
|
|
||||||
|
/* Destroy the framebuffers. */
|
||||||
|
for (unsigned int i = 0; i < NumFramebuffers; i ++) {
|
||||||
|
m_framebuffers_mem[i].destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Destroy the depth buffer. */
|
||||||
|
m_depth_buffer_mem.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecordStaticCommands() {
|
||||||
|
/* Initialize state structs with deko3d defaults. */
|
||||||
|
dk::RasterizerState rasterizer_state;
|
||||||
|
dk::ColorState color_state;
|
||||||
|
dk::ColorWriteState color_write_state;
|
||||||
|
|
||||||
|
/* Configure the viewport and scissor. */
|
||||||
|
m_cmd_buf.setViewports(0, { { 0.0f, 0.0f, FramebufferWidth, FramebufferHeight, 0.0f, 1.0f } });
|
||||||
|
m_cmd_buf.setScissors(0, { { 0, 0, FramebufferWidth, FramebufferHeight } });
|
||||||
|
|
||||||
|
/* Clear the color and depth buffers. */
|
||||||
|
m_cmd_buf.clearColor(0, DkColorMask_RGBA, 0.f, 0.f, 0.f, 1.0f);
|
||||||
|
m_cmd_buf.clearDepthStencil(true, 1.0f, 0xFF, 0);
|
||||||
|
|
||||||
|
/* Bind required state. */
|
||||||
|
m_cmd_buf.bindRasterizerState(rasterizer_state);
|
||||||
|
m_cmd_buf.bindColorState(color_state);
|
||||||
|
m_cmd_buf.bindColorWriteState(color_write_state);
|
||||||
|
|
||||||
|
m_render_cmdlist = m_cmd_buf.finishList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Render(u64 ns) {
|
||||||
|
/* Acquire a framebuffer from the swapchain (and wait for it to be available). */
|
||||||
|
int slot = m_queue.acquireImage(m_swapchain);
|
||||||
|
|
||||||
|
/* Run the command list that attaches said framebuffer to the queue. */
|
||||||
|
m_queue.submitCommands(m_framebuffer_cmdlists[slot]);
|
||||||
|
|
||||||
|
/* Run the main rendering command list. */
|
||||||
|
m_queue.submitCommands(m_render_cmdlist);
|
||||||
|
|
||||||
|
nvgBeginFrame(m_vg, FramebufferWidth, FramebufferHeight, 1.0f);
|
||||||
|
dbk::RenderMenu(m_vg, ns);
|
||||||
|
nvgEndFrame(m_vg);
|
||||||
|
|
||||||
|
/* Now that we are done rendering, present it to the screen. */
|
||||||
|
m_queue.presentImage(m_swapchain, slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool onFrame(u64 ns) override {
|
||||||
|
dbk::UpdateMenu(ns);
|
||||||
|
this->Render(ns);
|
||||||
|
return !dbk::IsExitRequested();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
/* Initialize the menu. */
|
||||||
|
dbk::InitializeMenu(FramebufferWidth, FramebufferHeight);
|
||||||
|
|
||||||
|
Daybreak daybreak;
|
||||||
|
daybreak.run();
|
||||||
|
return 0;
|
||||||
|
}
|
56
troposphere/daybreak/source/service_guard.h
Normal file
56
troposphere/daybreak/source/service_guard.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#pragma once
|
||||||
|
#include <switch/types.h>
|
||||||
|
#include <switch/result.h>
|
||||||
|
#include <switch/kernel/mutex.h>
|
||||||
|
#include <switch/sf/service.h>
|
||||||
|
#include <switch/services/sm.h>
|
||||||
|
|
||||||
|
typedef struct ServiceGuard {
|
||||||
|
Mutex mutex;
|
||||||
|
u32 refCount;
|
||||||
|
} ServiceGuard;
|
||||||
|
|
||||||
|
NX_INLINE bool serviceGuardBeginInit(ServiceGuard* g)
|
||||||
|
{
|
||||||
|
mutexLock(&g->mutex);
|
||||||
|
return (g->refCount++) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
NX_INLINE Result serviceGuardEndInit(ServiceGuard* g, Result rc, void (*cleanupFunc)(void))
|
||||||
|
{
|
||||||
|
if (R_FAILED(rc)) {
|
||||||
|
cleanupFunc();
|
||||||
|
--g->refCount;
|
||||||
|
}
|
||||||
|
mutexUnlock(&g->mutex);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
NX_INLINE void serviceGuardExit(ServiceGuard* g, void (*cleanupFunc)(void))
|
||||||
|
{
|
||||||
|
mutexLock(&g->mutex);
|
||||||
|
if (g->refCount && (--g->refCount) == 0)
|
||||||
|
cleanupFunc();
|
||||||
|
mutexUnlock(&g->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NX_GENERATE_SERVICE_GUARD_PARAMS(name, _paramdecl, _parampass) \
|
||||||
|
\
|
||||||
|
static ServiceGuard g_##name##Guard; \
|
||||||
|
NX_INLINE Result _##name##Initialize _paramdecl; \
|
||||||
|
static void _##name##Cleanup(void); \
|
||||||
|
\
|
||||||
|
Result name##Initialize _paramdecl \
|
||||||
|
{ \
|
||||||
|
Result rc = 0; \
|
||||||
|
if (serviceGuardBeginInit(&g_##name##Guard)) \
|
||||||
|
rc = _##name##Initialize _parampass; \
|
||||||
|
return serviceGuardEndInit(&g_##name##Guard, rc, _##name##Cleanup); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
void name##Exit(void) \
|
||||||
|
{ \
|
||||||
|
serviceGuardExit(&g_##name##Guard, _##name##Cleanup); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NX_GENERATE_SERVICE_GUARD(name) NX_GENERATE_SERVICE_GUARD_PARAMS(name, (void), ())
|
969
troposphere/daybreak/source/ui.cpp
Normal file
969
troposphere/daybreak/source/ui.cpp
Normal file
|
@ -0,0 +1,969 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Adubbz
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include "ui.hpp"
|
||||||
|
#include "ui_util.hpp"
|
||||||
|
#include "assert.hpp"
|
||||||
|
|
||||||
|
namespace dbk {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
u32 g_screen_width;
|
||||||
|
u32 g_screen_height;
|
||||||
|
|
||||||
|
std::shared_ptr<Menu> g_current_menu;
|
||||||
|
bool g_initialized = false;
|
||||||
|
bool g_exit_requested = false;
|
||||||
|
|
||||||
|
u32 g_prev_touch_count = -1;
|
||||||
|
touchPosition g_start_touch_position;
|
||||||
|
bool g_started_touching = false;
|
||||||
|
bool g_tapping = false;
|
||||||
|
bool g_touches_moving = false;
|
||||||
|
bool g_finished_touching = false;
|
||||||
|
|
||||||
|
/* Update install state. */
|
||||||
|
char g_update_path[FS_MAX_PATH];
|
||||||
|
bool g_use_exfat = false;
|
||||||
|
|
||||||
|
constexpr u32 MaxTapMovement = 20;
|
||||||
|
|
||||||
|
void UpdateInput() {
|
||||||
|
/* Update the previous touch count. */
|
||||||
|
g_prev_touch_count = hidTouchCount();
|
||||||
|
|
||||||
|
/* Scan for input and update touch state. */
|
||||||
|
hidScanInput();
|
||||||
|
const u32 touch_count = hidTouchCount();
|
||||||
|
|
||||||
|
if (g_prev_touch_count == 0 && touch_count > 0) {
|
||||||
|
hidTouchRead(&g_start_touch_position, 0);
|
||||||
|
g_started_touching = true;
|
||||||
|
g_tapping = true;
|
||||||
|
} else {
|
||||||
|
g_started_touching = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_prev_touch_count > 0 && touch_count == 0) {
|
||||||
|
g_finished_touching = true;
|
||||||
|
g_tapping = false;
|
||||||
|
} else {
|
||||||
|
g_finished_touching = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if currently moving. */
|
||||||
|
if (g_prev_touch_count > 0 && touch_count > 0) {
|
||||||
|
touchPosition current_touch_position;
|
||||||
|
hidTouchRead(¤t_touch_position, 0);
|
||||||
|
|
||||||
|
if ((abs(current_touch_position.px - g_start_touch_position.px) > MaxTapMovement || abs(current_touch_position.py - g_start_touch_position.py) > MaxTapMovement)) {
|
||||||
|
g_touches_moving = true;
|
||||||
|
g_tapping = false;
|
||||||
|
} else {
|
||||||
|
g_touches_moving = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
g_touches_moving = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChangeMenu(std::shared_ptr<Menu> menu) {
|
||||||
|
g_current_menu = menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReturnToPreviousMenu() {
|
||||||
|
/* Go to the previous menu if there is one. */
|
||||||
|
if (g_current_menu->GetPrevMenu() != nullptr) {
|
||||||
|
g_current_menu = g_current_menu->GetPrevMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result IsPathBottomLevel(const char *path, bool *out) {
|
||||||
|
Result rc = 0;
|
||||||
|
FsFileSystem *fs;
|
||||||
|
char translated_path[FS_MAX_PATH] = {};
|
||||||
|
DBK_ABORT_UNLESS(fsdevTranslatePath(path, &fs, translated_path) != -1);
|
||||||
|
|
||||||
|
FsDir dir;
|
||||||
|
if (R_FAILED(rc = fsFsOpenDirectory(fs, translated_path, FsDirOpenMode_ReadDirs, &dir))) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
s64 entry_count;
|
||||||
|
if (R_FAILED(rc = fsDirGetEntryCount(&dir, &entry_count))) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = entry_count == 0;
|
||||||
|
fsDirClose(&dir);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::AddButton(u32 id, const char *text, float x, float y, float w, float h) {
|
||||||
|
DBK_ABORT_UNLESS(id < MaxButtons);
|
||||||
|
Button button = {
|
||||||
|
.id = id,
|
||||||
|
.selected = false,
|
||||||
|
.enabled = true,
|
||||||
|
.x = x,
|
||||||
|
.y = y,
|
||||||
|
.w = w,
|
||||||
|
.h = h,
|
||||||
|
};
|
||||||
|
|
||||||
|
strncpy(button.text, text, sizeof(button.text)-1);
|
||||||
|
m_buttons[id] = button;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::SetButtonSelected(u32 id, bool selected) {
|
||||||
|
DBK_ABORT_UNLESS(id < MaxButtons);
|
||||||
|
auto &button = m_buttons[id];
|
||||||
|
|
||||||
|
if (button) {
|
||||||
|
button->selected = selected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::DeselectAllButtons() {
|
||||||
|
for (auto &button : m_buttons) {
|
||||||
|
/* Ensure button is present. */
|
||||||
|
if (!button) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
button->selected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::SetButtonEnabled(u32 id, bool enabled) {
|
||||||
|
DBK_ABORT_UNLESS(id < MaxButtons);
|
||||||
|
auto &button = m_buttons[id];
|
||||||
|
button->enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
Button *Menu::GetButton(u32 id) {
|
||||||
|
DBK_ABORT_UNLESS(id < MaxButtons);
|
||||||
|
return !m_buttons[id] ? nullptr : &(*m_buttons[id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Button *Menu::GetSelectedButton() {
|
||||||
|
for (auto &button : m_buttons) {
|
||||||
|
if (button && button->enabled && button->selected) {
|
||||||
|
return &(*button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Button *Menu::GetClosestButtonToSelection(Direction direction) {
|
||||||
|
const Button *selected_button = this->GetSelectedButton();
|
||||||
|
|
||||||
|
if (selected_button == nullptr || direction == Direction::Invalid) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Button *closest_button = nullptr;
|
||||||
|
float closest_distance = 0.0f;
|
||||||
|
|
||||||
|
for (auto &button : m_buttons) {
|
||||||
|
/* Skip absent button. */
|
||||||
|
if (!button || !button->enabled) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Skip buttons that are in the wrong direction. */
|
||||||
|
if ((direction == Direction::Down && button->y <= selected_button->y) ||
|
||||||
|
(direction == Direction::Up && button->y >= selected_button->y) ||
|
||||||
|
(direction == Direction::Right && button->x <= selected_button->x) ||
|
||||||
|
(direction == Direction::Left && button->x >= selected_button->x)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float x_dist = button->x - selected_button->x;
|
||||||
|
const float y_dist = button->y - selected_button->y;
|
||||||
|
const float sq_dist = x_dist * x_dist + y_dist * y_dist;
|
||||||
|
|
||||||
|
/* If we don't already have a closest button, set it. */
|
||||||
|
if (closest_button == nullptr) {
|
||||||
|
closest_button = &(*button);
|
||||||
|
closest_distance = sq_dist;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the closest button if this one is closer. */
|
||||||
|
if (sq_dist < closest_distance) {
|
||||||
|
closest_button = &(*button);
|
||||||
|
closest_distance = sq_dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return closest_button;
|
||||||
|
}
|
||||||
|
|
||||||
|
Button *Menu::GetTouchedButton() {
|
||||||
|
touchPosition touch;
|
||||||
|
const u32 touch_count = hidTouchCount();
|
||||||
|
|
||||||
|
for (u32 i = 0; i < touch_count && g_started_touching; i++) {
|
||||||
|
hidTouchRead(&touch, i);
|
||||||
|
|
||||||
|
for (auto &button : m_buttons) {
|
||||||
|
if (button && button->enabled && button->IsPositionInBounds(touch.px, touch.py)) {
|
||||||
|
return &(*button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Button *Menu::GetActivatedButton() {
|
||||||
|
Button *selected_button = this->GetSelectedButton();
|
||||||
|
|
||||||
|
if (selected_button == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u64 k_down = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||||
|
|
||||||
|
if (k_down & KEY_A || this->GetTouchedButton() == selected_button) {
|
||||||
|
return selected_button;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::UpdateButtons() {
|
||||||
|
const u64 k_down = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||||
|
Direction direction = Direction::Invalid;
|
||||||
|
|
||||||
|
if (k_down & KEY_DOWN) {
|
||||||
|
direction = Direction::Down;
|
||||||
|
} else if (k_down & KEY_UP) {
|
||||||
|
direction = Direction::Up;
|
||||||
|
} else if (k_down & KEY_LEFT) {
|
||||||
|
direction = Direction::Left;
|
||||||
|
} else if (k_down & KEY_RIGHT) {
|
||||||
|
direction = Direction::Right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select the closest button. */
|
||||||
|
if (const Button *closest_button = this->GetClosestButtonToSelection(direction); closest_button != nullptr) {
|
||||||
|
this->DeselectAllButtons();
|
||||||
|
this->SetButtonSelected(closest_button->id, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select the touched button. */
|
||||||
|
if (const Button *touched_button = this->GetTouchedButton(); touched_button != nullptr) {
|
||||||
|
this->DeselectAllButtons();
|
||||||
|
this->SetButtonSelected(touched_button->id, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::DrawButtons(NVGcontext *vg, u64 ns) {
|
||||||
|
for (auto &button : m_buttons) {
|
||||||
|
/* Ensure button is present. */
|
||||||
|
if (!button) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the button style. */
|
||||||
|
auto style = ButtonStyle::StandardDisabled;
|
||||||
|
if (button->enabled) {
|
||||||
|
style = button->selected ? ButtonStyle::StandardSelected : ButtonStyle::Standard;
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawButton(vg, button->text, button->x, button->y, button->w, button->h, style, ns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Menu::LogText(const char *format, ...) {
|
||||||
|
/* Create a temporary string. */
|
||||||
|
char tmp[0x100];
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
vsnprintf(tmp, sizeof(tmp)-1, format, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
/* Append the text to the log buffer. */
|
||||||
|
strncat(m_log_buffer, tmp, sizeof(m_log_buffer)-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Menu> Menu::GetPrevMenu() {
|
||||||
|
return m_prev_menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
MainMenu::MainMenu() : Menu(nullptr) {
|
||||||
|
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f;
|
||||||
|
const float y = g_screen_height / 2.0f - WindowHeight / 2.0f;
|
||||||
|
|
||||||
|
this->AddButton(InstallButtonId, "Install", x + ButtonHorizontalPadding, y + TitleGap, WindowWidth - ButtonHorizontalPadding * 2, ButtonHeight);
|
||||||
|
this->AddButton(ExitButtonId, "Exit", x + ButtonHorizontalPadding, y + TitleGap + ButtonHeight + ButtonVerticalGap, WindowWidth - ButtonHorizontalPadding * 2, ButtonHeight);
|
||||||
|
this->SetButtonSelected(InstallButtonId, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainMenu::Update(u64 ns) {
|
||||||
|
u64 k_down = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||||
|
|
||||||
|
if (k_down & KEY_B) {
|
||||||
|
g_exit_requested = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Take action if a button has been activated. */
|
||||||
|
if (const Button *activated_button = this->GetActivatedButton(); activated_button != nullptr) {
|
||||||
|
switch (activated_button->id) {
|
||||||
|
case InstallButtonId:
|
||||||
|
ChangeMenu(std::make_shared<FileMenu>(g_current_menu, "/"));
|
||||||
|
break;
|
||||||
|
case ExitButtonId:
|
||||||
|
g_exit_requested = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->UpdateButtons();
|
||||||
|
|
||||||
|
/* Fallback on selecting the install button. */
|
||||||
|
if (const Button *selected_button = this->GetSelectedButton(); k_down && selected_button == nullptr) {
|
||||||
|
this->SetButtonSelected(InstallButtonId, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainMenu::Draw(NVGcontext *vg, u64 ns) {
|
||||||
|
DrawWindow(vg, "Daybreak", g_screen_width / 2.0f - WindowWidth / 2.0f, g_screen_height / 2.0f - WindowHeight / 2.0f, WindowWidth, WindowHeight);
|
||||||
|
this->DrawButtons(vg, ns);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileMenu::FileMenu(std::shared_ptr<Menu> prev_menu, const char *root) : Menu(prev_menu), m_current_index(0), m_scroll_offset(0), m_touch_start_scroll_offset(0), m_touch_finalize_selection(false) {
|
||||||
|
Result rc = 0;
|
||||||
|
|
||||||
|
strncpy(m_root, root, sizeof(m_root)-1);
|
||||||
|
|
||||||
|
if (R_FAILED(rc = this->PopulateFileEntries())) {
|
||||||
|
fatalThrow(rc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result FileMenu::PopulateFileEntries() {
|
||||||
|
/* Open the directory. */
|
||||||
|
DIR *dir = opendir(m_root);
|
||||||
|
if (dir == nullptr) {
|
||||||
|
return fsdevGetLastResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add file entries to the list. */
|
||||||
|
struct dirent *ent;
|
||||||
|
while ((ent = readdir(dir)) != nullptr) {
|
||||||
|
if (ent->d_type == DT_DIR) {
|
||||||
|
FileEntry file_entry = {};
|
||||||
|
strncpy(file_entry.name, ent->d_name, sizeof(file_entry.name));
|
||||||
|
m_file_entries.push_back(file_entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close the directory. */
|
||||||
|
closedir(dir);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileMenu::IsSelectionVisible() {
|
||||||
|
const float visible_start = m_scroll_offset;
|
||||||
|
const float visible_end = visible_start + FileListHeight;
|
||||||
|
const float entry_start = static_cast<float>(m_current_index) * (FileRowHeight + FileRowGap);
|
||||||
|
const float entry_end = entry_start + (FileRowHeight + FileRowGap);
|
||||||
|
return entry_start >= visible_start && entry_end <= visible_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileMenu::ScrollToSelection() {
|
||||||
|
const float visible_start = m_scroll_offset;
|
||||||
|
const float visible_end = visible_start + FileListHeight;
|
||||||
|
const float entry_start = static_cast<float>(m_current_index) * (FileRowHeight + FileRowGap);
|
||||||
|
const float entry_end = entry_start + (FileRowHeight + FileRowGap);
|
||||||
|
|
||||||
|
if (entry_end > visible_end) {
|
||||||
|
m_scroll_offset += entry_end - visible_end;
|
||||||
|
} else if (entry_end < visible_end) {
|
||||||
|
m_scroll_offset = entry_start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileMenu::IsEntryTouched(u32 i) {
|
||||||
|
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f;
|
||||||
|
const float y = g_screen_height / 2.0f - WindowHeight / 2.0f;
|
||||||
|
|
||||||
|
touchPosition current_pos;
|
||||||
|
hidTouchRead(¤t_pos, 0);
|
||||||
|
|
||||||
|
/* Check if the tap is within the x bounds. */
|
||||||
|
if (current_pos.px >= x + TextBackgroundOffset + FileRowHorizontalInset && current_pos.px <= WindowWidth - (TextBackgroundOffset + FileRowHorizontalInset) * 2.0f) {
|
||||||
|
const float y_min = y + TitleGap + FileRowGap + i * (FileRowHeight + FileRowGap) - m_scroll_offset;
|
||||||
|
const float y_max = y_min + FileRowHeight;
|
||||||
|
|
||||||
|
/* Check if the tap is within the y bounds. */
|
||||||
|
if (current_pos.py >= y_min && current_pos.py <= y_max) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileMenu::UpdateTouches() {
|
||||||
|
/* Setup values on initial touch. */
|
||||||
|
if (g_started_touching) {
|
||||||
|
m_touch_start_scroll_offset = m_scroll_offset;
|
||||||
|
|
||||||
|
/* We may potentially finalize the selection later if we start off touching it. */
|
||||||
|
if (this->IsEntryTouched(m_current_index)) {
|
||||||
|
m_touch_finalize_selection = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scroll based on touch movement. */
|
||||||
|
if (g_touches_moving) {
|
||||||
|
touchPosition current_pos;
|
||||||
|
hidTouchRead(¤t_pos, 0);
|
||||||
|
|
||||||
|
const int dist_y = current_pos.py - g_start_touch_position.py;
|
||||||
|
float new_scroll_offset = m_touch_start_scroll_offset - static_cast<float>(dist_y);
|
||||||
|
float max_scroll = (FileRowHeight + FileRowGap) * static_cast<float>(m_file_entries.size()) - FileListHeight;
|
||||||
|
|
||||||
|
/* Don't allow scrolling if there is not enough elements. */
|
||||||
|
if (max_scroll < 0.0f) {
|
||||||
|
max_scroll = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Don't allow scrolling before the first element. */
|
||||||
|
if (new_scroll_offset < 0.0f) {
|
||||||
|
new_scroll_offset = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Don't allow scrolling past the last element. */
|
||||||
|
if (new_scroll_offset > max_scroll) {
|
||||||
|
new_scroll_offset = max_scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_scroll_offset = new_scroll_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select any tapped entries. */
|
||||||
|
if (g_tapping) {
|
||||||
|
for (u32 i = 0; i < m_file_entries.size(); i++) {
|
||||||
|
if (this->IsEntryTouched(i)) {
|
||||||
|
/* The current index is checked later. */
|
||||||
|
if (i == m_current_index) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_current_index = i;
|
||||||
|
|
||||||
|
/* Don't finalize selection if we touch something else. */
|
||||||
|
m_touch_finalize_selection = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Don't finalize selection if we aren't finished and we've either stopped tapping or are no longer touching the selection. */
|
||||||
|
if (!g_finished_touching && (!g_tapping || !this->IsEntryTouched(m_current_index))) {
|
||||||
|
m_touch_finalize_selection = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finalize selection if the currently selected entry is touched for the second time. */
|
||||||
|
if (g_finished_touching && m_touch_finalize_selection) {
|
||||||
|
this->FinalizeSelection();
|
||||||
|
m_touch_finalize_selection = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileMenu::FinalizeSelection() {
|
||||||
|
DBK_ABORT_UNLESS(m_current_index < m_file_entries.size());
|
||||||
|
FileEntry &entry = m_file_entries[m_current_index];
|
||||||
|
|
||||||
|
/* Determine the selected path. */
|
||||||
|
char current_path[FS_MAX_PATH] = {};
|
||||||
|
snprintf(current_path, sizeof(current_path)-1, "%s%s", m_root, entry.name);
|
||||||
|
|
||||||
|
/* Determine if the chosen path is the bottom level. */
|
||||||
|
Result rc = 0;
|
||||||
|
bool bottom_level;
|
||||||
|
if (R_FAILED(rc = IsPathBottomLevel(current_path, &bottom_level))) {
|
||||||
|
fatalThrow(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show exfat settings or the next file menu. */
|
||||||
|
if (bottom_level) {
|
||||||
|
/* Set the update path. */
|
||||||
|
snprintf(g_update_path, sizeof(g_update_path)-1, "%s", current_path);
|
||||||
|
|
||||||
|
/* Change the menu. */
|
||||||
|
ChangeMenu(std::make_shared<ValidateUpdateMenu>(g_current_menu));
|
||||||
|
} else {
|
||||||
|
ChangeMenu(std::make_shared<FileMenu>(g_current_menu, current_path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileMenu::Update(u64 ns) {
|
||||||
|
u64 k_down = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||||
|
|
||||||
|
/* Go back if B is pressed. */
|
||||||
|
if (k_down & KEY_B) {
|
||||||
|
ReturnToPreviousMenu();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finalize selection on pressing A. */
|
||||||
|
if (k_down & KEY_A) {
|
||||||
|
this->FinalizeSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update touch input. */
|
||||||
|
this->UpdateTouches();
|
||||||
|
|
||||||
|
const u32 prev_index = m_current_index;
|
||||||
|
|
||||||
|
if (k_down & KEY_DOWN) {
|
||||||
|
/* Scroll down. */
|
||||||
|
if (m_current_index >= (m_file_entries.size() - 1)) {
|
||||||
|
m_current_index = 0;
|
||||||
|
} else {
|
||||||
|
m_current_index++;
|
||||||
|
}
|
||||||
|
} else if (k_down & KEY_UP) {
|
||||||
|
/* Scroll up. */
|
||||||
|
if (m_current_index == 0) {
|
||||||
|
m_current_index = m_file_entries.size() - 1;
|
||||||
|
} else {
|
||||||
|
m_current_index--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scroll to the selection if it isn't visible. */
|
||||||
|
if (prev_index != m_current_index && !this->IsSelectionVisible()) {
|
||||||
|
this->ScrollToSelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileMenu::Draw(NVGcontext *vg, u64 ns) {
|
||||||
|
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f;
|
||||||
|
const float y = g_screen_height / 2.0f - WindowHeight / 2.0f;
|
||||||
|
|
||||||
|
DrawWindow(vg, "Select an update directory", x, y, WindowWidth, WindowHeight);
|
||||||
|
DrawTextBackground(vg, x + TextBackgroundOffset, y + TitleGap, WindowWidth - TextBackgroundOffset * 2.0f, (FileRowHeight + FileRowGap) * MaxFileRows + FileRowGap);
|
||||||
|
|
||||||
|
nvgSave(vg);
|
||||||
|
nvgScissor(vg, x + TextBackgroundOffset, y + TitleGap, WindowWidth - TextBackgroundOffset * 2.0f, (FileRowHeight + FileRowGap) * MaxFileRows + FileRowGap);
|
||||||
|
|
||||||
|
for (u32 i = 0; i < m_file_entries.size(); i++) {
|
||||||
|
FileEntry &entry = m_file_entries[i];
|
||||||
|
auto style = ButtonStyle::FileSelect;
|
||||||
|
|
||||||
|
if (i == m_current_index) {
|
||||||
|
style = ButtonStyle::FileSelectSelected;
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawButton(vg, entry.name, x + TextBackgroundOffset + FileRowHorizontalInset, y + TitleGap + FileRowGap + i * (FileRowHeight + FileRowGap) - m_scroll_offset, WindowWidth - (TextBackgroundOffset + FileRowHorizontalInset) * 2.0f, FileRowHeight, style, ns);
|
||||||
|
}
|
||||||
|
|
||||||
|
nvgRestore(vg);
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidateUpdateMenu::ValidateUpdateMenu(std::shared_ptr<Menu> prev_menu) : Menu(prev_menu), m_has_drawn(false), m_has_info(false), m_has_validated(false) {
|
||||||
|
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f;
|
||||||
|
const float y = g_screen_height / 2.0f - WindowHeight / 2.0f;
|
||||||
|
|
||||||
|
/* Add buttons. */
|
||||||
|
this->AddButton(BackButtonId, "Back", x + HorizontalGap, y + WindowHeight - BottomGap - ButtonHeight, ButtonWidth, ButtonHeight);
|
||||||
|
this->AddButton(ContinueButtonId, "Continue", x + HorizontalGap + ButtonWidth + ButtonHorizontalGap, y + WindowHeight - BottomGap - ButtonHeight, ButtonWidth, ButtonHeight);
|
||||||
|
this->SetButtonEnabled(BackButtonId, false);
|
||||||
|
this->SetButtonEnabled(ContinueButtonId, false);
|
||||||
|
|
||||||
|
/* Obtain update information. */
|
||||||
|
if (R_FAILED(this->GetUpdateInformation())) {
|
||||||
|
this->SetButtonEnabled(BackButtonId, true);
|
||||||
|
this->SetButtonSelected(BackButtonId, true);
|
||||||
|
} else {
|
||||||
|
/* Log this early so it is printed out before validation causes stalling. */
|
||||||
|
this->LogText("Validating update, this may take a moment...\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ValidateUpdateMenu::GetUpdateInformation() {
|
||||||
|
Result rc = 0;
|
||||||
|
this->LogText("Directory %s\n", g_update_path);
|
||||||
|
|
||||||
|
/* Attempt to get the update information. */
|
||||||
|
if (R_FAILED(rc = amssuGetUpdateInformation(&m_update_info, g_update_path))) {
|
||||||
|
if (rc == 0x1a405) {
|
||||||
|
this->LogText("No update found in folder.\nEnsure your ncas are named correctly!\nResult: 0x%08x\n", rc);
|
||||||
|
} else {
|
||||||
|
this->LogText("Failed to get update information.\nResult: 0x%08x\n", rc);
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print update information. */
|
||||||
|
this->LogText("- Version: %d.%d.%d\n", (m_update_info.version >> 26) & 0x1f, (m_update_info.version >> 20) & 0x1f, (m_update_info.version >> 16) & 0xf);
|
||||||
|
if (m_update_info.exfat_supported) {
|
||||||
|
this->LogText("- exFAT: Supported\n");
|
||||||
|
} else {
|
||||||
|
this->LogText("- exFAT: Unsupported\n");
|
||||||
|
}
|
||||||
|
this->LogText("- Firmware variations: %d\n", m_update_info.num_firmware_variations);
|
||||||
|
|
||||||
|
/* Mark as having obtained update info. */
|
||||||
|
m_has_info = true;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValidateUpdateMenu::ValidateUpdate() {
|
||||||
|
Result rc = 0;
|
||||||
|
|
||||||
|
/* Validate the update. */
|
||||||
|
if (R_FAILED(rc = amssuValidateUpdate(&m_validation_info, g_update_path))) {
|
||||||
|
this->LogText("Failed to validate update.\nResult: 0x%08x\n", rc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check the result. */
|
||||||
|
if (R_SUCCEEDED(m_validation_info.result)) {
|
||||||
|
this->LogText("Update is valid!\n");
|
||||||
|
|
||||||
|
/* Enable the back and continue buttons and select the continue button. */
|
||||||
|
this->SetButtonEnabled(BackButtonId, true);
|
||||||
|
this->SetButtonEnabled(ContinueButtonId, true);
|
||||||
|
this->SetButtonSelected(ContinueButtonId, true);
|
||||||
|
} else {
|
||||||
|
/* Log the missing content info. */
|
||||||
|
const u32 version = m_validation_info.invalid_key.version;
|
||||||
|
this->LogText("Validation failed with result: 0x%08x\n", m_validation_info.result);
|
||||||
|
this->LogText("Missing content:\n- Program id: %016lx\n- Version: %d.%d.%d\n", m_validation_info.invalid_key.id, (version >> 26) & 0x1f, (version >> 20) & 0x1f, (version >> 16) & 0xf);
|
||||||
|
|
||||||
|
/* Log the missing content id. */
|
||||||
|
this->LogText("- Content id: ");
|
||||||
|
for (size_t i = 0; i < sizeof(NcmContentId); i++) {
|
||||||
|
this->LogText("%02x", m_validation_info.invalid_content_id.c[i]);
|
||||||
|
}
|
||||||
|
this->LogText("\n");
|
||||||
|
|
||||||
|
/* Enable the back button and select it. */
|
||||||
|
this->SetButtonEnabled(BackButtonId, true);
|
||||||
|
this->SetButtonSelected(BackButtonId, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mark validation as being complete. */
|
||||||
|
m_has_validated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValidateUpdateMenu::Update(u64 ns) {
|
||||||
|
/* Perform validation if it hasn't been done already. */
|
||||||
|
if (m_has_info && m_has_drawn && !m_has_validated) {
|
||||||
|
this->ValidateUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 k_down = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||||
|
|
||||||
|
/* Go back if B is pressed. */
|
||||||
|
if (k_down & KEY_B) {
|
||||||
|
ReturnToPreviousMenu();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Take action if a button has been activated. */
|
||||||
|
if (const Button *activated_button = this->GetActivatedButton(); activated_button != nullptr) {
|
||||||
|
switch (activated_button->id) {
|
||||||
|
case BackButtonId:
|
||||||
|
ReturnToPreviousMenu();
|
||||||
|
return;
|
||||||
|
case ContinueButtonId:
|
||||||
|
/* Don't continue if validation hasn't been done or has failed. */
|
||||||
|
if (!m_has_validated || R_FAILED(m_validation_info.result)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_update_info.exfat_supported) {
|
||||||
|
ChangeMenu(std::make_shared<ChooseExfatMenu>(g_current_menu));
|
||||||
|
} else {
|
||||||
|
g_use_exfat = false;
|
||||||
|
ChangeMenu(std::make_shared<InstallUpdateMenu>(g_current_menu));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->UpdateButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValidateUpdateMenu::Draw(NVGcontext *vg, u64 ns) {
|
||||||
|
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f;
|
||||||
|
const float y = g_screen_height / 2.0f - WindowHeight / 2.0f;
|
||||||
|
|
||||||
|
DrawWindow(vg, "Update information", x, y, WindowWidth, WindowHeight);
|
||||||
|
DrawTextBackground(vg, x + HorizontalGap, y + TitleGap, WindowWidth - HorizontalGap * 2.0f, TextAreaHeight);
|
||||||
|
DrawTextBlock(vg, m_log_buffer, x + HorizontalGap + TextHorizontalInset, y + TitleGap + TextVerticalInset, WindowWidth - (HorizontalGap + TextHorizontalInset) * 2.0f, TextAreaHeight - TextVerticalInset * 2.0f);
|
||||||
|
|
||||||
|
this->DrawButtons(vg, ns);
|
||||||
|
m_has_drawn = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChooseExfatMenu::ChooseExfatMenu(std::shared_ptr<Menu> prev_menu) : Menu(prev_menu) {
|
||||||
|
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f;
|
||||||
|
const float y = g_screen_height / 2.0f - WindowHeight / 2.0f;
|
||||||
|
|
||||||
|
this->AddButton(Fat32ButtonId, "FAT32", x + ButtonHorizontalInset, y + TitleGap, ButtonWidth, ButtonHeight);
|
||||||
|
this->AddButton(ExFatButtonId, "exFAT", x + ButtonHorizontalInset + ButtonWidth + ButtonHorizontalGap, y + TitleGap, ButtonWidth, ButtonHeight);
|
||||||
|
this->SetButtonSelected(ExFatButtonId, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChooseExfatMenu::Update(u64 ns) {
|
||||||
|
u64 k_down = hidKeysDown(CONTROLLER_P1_AUTO);
|
||||||
|
|
||||||
|
/* Go back if B is pressed. */
|
||||||
|
if (k_down & KEY_B) {
|
||||||
|
ReturnToPreviousMenu();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Take action if a button has been activated. */
|
||||||
|
if (const Button *activated_button = this->GetActivatedButton(); activated_button != nullptr) {
|
||||||
|
switch (activated_button->id) {
|
||||||
|
case Fat32ButtonId:
|
||||||
|
g_use_exfat = false;
|
||||||
|
break;
|
||||||
|
case ExFatButtonId:
|
||||||
|
g_use_exfat = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChangeMenu(std::make_shared<InstallUpdateMenu>(g_current_menu));
|
||||||
|
}
|
||||||
|
|
||||||
|
this->UpdateButtons();
|
||||||
|
|
||||||
|
/* Fallback on selecting the exfat button. */
|
||||||
|
if (const Button *selected_button = this->GetSelectedButton(); k_down && selected_button == nullptr) {
|
||||||
|
this->SetButtonSelected(ExFatButtonId, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChooseExfatMenu::Draw(NVGcontext *vg, u64 ns) {
|
||||||
|
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f;
|
||||||
|
const float y = g_screen_height / 2.0f - WindowHeight / 2.0f;
|
||||||
|
|
||||||
|
DrawWindow(vg, "Select driver variant", x, y, WindowWidth, WindowHeight);
|
||||||
|
this->DrawButtons(vg, ns);
|
||||||
|
}
|
||||||
|
|
||||||
|
InstallUpdateMenu::InstallUpdateMenu(std::shared_ptr<Menu> prev_menu) : Menu(prev_menu), m_install_state(InstallState::NeedsDraw), m_progress_percent(0.0f) {
|
||||||
|
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f;
|
||||||
|
const float y = g_screen_height / 2.0f - WindowHeight / 2.0f;
|
||||||
|
|
||||||
|
/* Add buttons. */
|
||||||
|
this->AddButton(ShutdownButtonId, "Shutdown", x + HorizontalGap, y + WindowHeight - BottomGap - ButtonHeight, ButtonWidth, ButtonHeight);
|
||||||
|
this->AddButton(RebootButtonId, "Reboot", x + HorizontalGap + ButtonWidth + ButtonHorizontalGap, y + WindowHeight - BottomGap - ButtonHeight, ButtonWidth, ButtonHeight);
|
||||||
|
this->SetButtonEnabled(ShutdownButtonId, false);
|
||||||
|
this->SetButtonEnabled(RebootButtonId, false);
|
||||||
|
|
||||||
|
/* Prevent the home button from being pressed during installation. */
|
||||||
|
hiddbgDeactivateHomeButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstallUpdateMenu::MarkForReboot() {
|
||||||
|
this->SetButtonEnabled(ShutdownButtonId, true);
|
||||||
|
this->SetButtonEnabled(RebootButtonId, true);
|
||||||
|
this->SetButtonSelected(RebootButtonId, true);
|
||||||
|
m_install_state = InstallState::AwaitingReboot;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InstallUpdateMenu::TransitionUpdateState() {
|
||||||
|
Result rc = 0;
|
||||||
|
if (m_install_state == InstallState::NeedsSetup) {
|
||||||
|
/* Setup the update. */
|
||||||
|
if (R_FAILED(rc = amssuSetupUpdate(nullptr, UpdateTaskBufferSize, g_update_path, g_use_exfat))) {
|
||||||
|
this->LogText("Failed to setup update.\nResult: 0x%08x\n", rc);
|
||||||
|
this->MarkForReboot();
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Log setup completion. */
|
||||||
|
this->LogText("Update setup complete.\n");
|
||||||
|
m_install_state = InstallState::NeedsPrepare;
|
||||||
|
} else if (m_install_state == InstallState::NeedsPrepare) {
|
||||||
|
/* Request update preparation. */
|
||||||
|
if (R_FAILED(rc = amssuRequestPrepareUpdate(&m_prepare_result))) {
|
||||||
|
this->LogText("Failed to request update preparation.\nResult: 0x%08x\n", rc);
|
||||||
|
this->MarkForReboot();
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Log awaiting prepare. */
|
||||||
|
this->LogText("Preparing update...\n");
|
||||||
|
m_install_state = InstallState::AwaitingPrepare;
|
||||||
|
} else if (m_install_state == InstallState::AwaitingPrepare) {
|
||||||
|
/* Check if preparation has a result. */
|
||||||
|
if (R_FAILED(rc = asyncResultWait(&m_prepare_result, 0)) && rc != 0xea01) {
|
||||||
|
this->LogText("Failed to check update preparation result.\nResult: 0x%08x\n", rc);
|
||||||
|
this->MarkForReboot();
|
||||||
|
return rc;
|
||||||
|
} else if (R_SUCCEEDED(rc)) {
|
||||||
|
if (R_FAILED(rc = asyncResultGet(&m_prepare_result))) {
|
||||||
|
this->LogText("Failed to prepare update.\nResult: 0x%08x\n", rc);
|
||||||
|
this->MarkForReboot();
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if the update has been prepared. */
|
||||||
|
bool prepared;
|
||||||
|
if (R_FAILED(rc = amssuHasPreparedUpdate(&prepared))) {
|
||||||
|
this->LogText("Failed to check if update has been prepared.\nResult: 0x%08x\n", rc);
|
||||||
|
this->MarkForReboot();
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mark for application if preparation complete. */
|
||||||
|
if (prepared) {
|
||||||
|
this->LogText("Update preparation complete.\nApplying update...\n");
|
||||||
|
m_install_state = InstallState::NeedsApply;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check update progress. */
|
||||||
|
NsSystemUpdateProgress update_progress = {};
|
||||||
|
if (R_FAILED(rc = amssuGetPrepareUpdateProgress(&update_progress))) {
|
||||||
|
this->LogText("Failed to check update progress.\nResult: 0x%08x\n", rc);
|
||||||
|
this->MarkForReboot();
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update progress percent. */
|
||||||
|
if (update_progress.total_size > 0.0f) {
|
||||||
|
m_progress_percent = static_cast<float>(update_progress.current_size) / static_cast<float>(update_progress.total_size);
|
||||||
|
} else {
|
||||||
|
m_progress_percent = 0.0f;
|
||||||
|
}
|
||||||
|
} else if (m_install_state == InstallState::NeedsApply) {
|
||||||
|
/* Apply the prepared update. */
|
||||||
|
if (R_FAILED(rc = amssuApplyPreparedUpdate())) {
|
||||||
|
this->LogText("Failed to apply update.\nResult: 0x%08x\n", rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Log success. */
|
||||||
|
this->LogText("Update applied successfully.\n");
|
||||||
|
this->MarkForReboot();
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstallUpdateMenu::Update(u64 ns) {
|
||||||
|
/* Transition to the next update state. */
|
||||||
|
if (m_install_state != InstallState::NeedsDraw && m_install_state != InstallState::AwaitingReboot) {
|
||||||
|
this->TransitionUpdateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Take action if a button has been activated. */
|
||||||
|
if (const Button *activated_button = this->GetActivatedButton(); activated_button != nullptr) {
|
||||||
|
switch (activated_button->id) {
|
||||||
|
case ShutdownButtonId:
|
||||||
|
if (R_FAILED(appletRequestToShutdown())) {
|
||||||
|
spsmShutdown(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case RebootButtonId:
|
||||||
|
if (R_FAILED(appletRequestToReboot())) {
|
||||||
|
spsmShutdown(true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->UpdateButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstallUpdateMenu::Draw(NVGcontext *vg, u64 ns) {
|
||||||
|
const float x = g_screen_width / 2.0f - WindowWidth / 2.0f;
|
||||||
|
const float y = g_screen_height / 2.0f - WindowHeight / 2.0f;
|
||||||
|
|
||||||
|
DrawWindow(vg, "Installing update", x, y, WindowWidth, WindowHeight);
|
||||||
|
DrawProgressText(vg, x + HorizontalGap, y + TitleGap, m_progress_percent);
|
||||||
|
DrawProgressBar(vg, x + HorizontalGap, y + TitleGap + ProgressTextHeight, WindowWidth - HorizontalGap * 2.0f, ProgressBarHeight, m_progress_percent);
|
||||||
|
DrawTextBackground(vg, x + HorizontalGap, y + TitleGap + ProgressTextHeight + ProgressBarHeight + VerticalGap, WindowWidth - HorizontalGap * 2.0f, TextAreaHeight);
|
||||||
|
DrawTextBlock(vg, m_log_buffer, x + HorizontalGap + TextHorizontalInset, y + TitleGap + ProgressTextHeight + ProgressBarHeight + VerticalGap + TextVerticalInset, WindowWidth - (HorizontalGap + TextHorizontalInset) * 2.0f, TextAreaHeight - TextVerticalInset * 2.0f);
|
||||||
|
|
||||||
|
this->DrawButtons(vg, ns);
|
||||||
|
|
||||||
|
/* We have drawn now, allow setup to occur. */
|
||||||
|
if (m_install_state == InstallState::NeedsDraw) {
|
||||||
|
this->LogText("Beginning update setup...\n");
|
||||||
|
m_install_state = InstallState::NeedsSetup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeMenu(u32 screen_width, u32 screen_height) {
|
||||||
|
/* Set the screen width and height. */
|
||||||
|
g_screen_width = screen_width;
|
||||||
|
g_screen_height = screen_height;
|
||||||
|
|
||||||
|
/* Change the current menu to the main menu. */
|
||||||
|
g_current_menu = std::make_shared<MainMenu>();
|
||||||
|
|
||||||
|
/* Mark as initialized. */
|
||||||
|
g_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateMenu(u64 ns) {
|
||||||
|
DBK_ABORT_UNLESS(g_initialized);
|
||||||
|
DBK_ABORT_UNLESS(g_current_menu != nullptr);
|
||||||
|
UpdateInput();
|
||||||
|
g_current_menu->Update(ns);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderMenu(NVGcontext *vg, u64 ns) {
|
||||||
|
DBK_ABORT_UNLESS(g_initialized);
|
||||||
|
DBK_ABORT_UNLESS(g_current_menu != nullptr);
|
||||||
|
|
||||||
|
/* Draw background. */
|
||||||
|
DrawBackground(vg, g_screen_width, g_screen_height);
|
||||||
|
|
||||||
|
/* Draw stars. */
|
||||||
|
DrawStar(vg, 40.0f, 64.0f, 3.0f);
|
||||||
|
DrawStar(vg, 110.0f, 300.0f, 3.0f);
|
||||||
|
DrawStar(vg, 200.0f, 150.0f, 4.0f);
|
||||||
|
DrawStar(vg, 370.0f, 280.0f, 3.0f);
|
||||||
|
DrawStar(vg, 450.0f, 40.0f, 3.5f);
|
||||||
|
DrawStar(vg, 710.0f, 90.0f, 3.0f);
|
||||||
|
DrawStar(vg, 900.0f, 240.0f, 3.0f);
|
||||||
|
DrawStar(vg, 970.0f, 64.0f, 4.0f);
|
||||||
|
DrawStar(vg, 1160.0f, 160.0f, 3.5f);
|
||||||
|
DrawStar(vg, 1210.0f, 350.0f, 3.0f);
|
||||||
|
|
||||||
|
g_current_menu->Draw(vg, ns);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsExitRequested() {
|
||||||
|
return g_exit_requested;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
240
troposphere/daybreak/source/ui.hpp
Normal file
240
troposphere/daybreak/source/ui.hpp
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Adubbz
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <optional>
|
||||||
|
#include <nanovg.h>
|
||||||
|
#include <switch.h>
|
||||||
|
#include "ams_su.h"
|
||||||
|
|
||||||
|
namespace dbk {
|
||||||
|
|
||||||
|
struct Button {
|
||||||
|
static constexpr u32 InvalidButtonId = -1;
|
||||||
|
|
||||||
|
u32 id;
|
||||||
|
bool selected;
|
||||||
|
bool enabled;
|
||||||
|
char text[256];
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float w;
|
||||||
|
float h;
|
||||||
|
|
||||||
|
inline bool IsPositionInBounds(float x, float y) {
|
||||||
|
return x >= this->x && y >= this->y && x < (this->x + this->w) && y < (this->y + this->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Direction {
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Invalid,
|
||||||
|
};
|
||||||
|
|
||||||
|
class Menu {
|
||||||
|
protected:
|
||||||
|
static constexpr size_t MaxButtons = 32;
|
||||||
|
static constexpr size_t LogBufferSize = 0x1000;
|
||||||
|
protected:
|
||||||
|
std::array<std::optional<Button>, MaxButtons> m_buttons;
|
||||||
|
const std::shared_ptr<Menu> m_prev_menu;
|
||||||
|
char m_log_buffer[LogBufferSize];
|
||||||
|
protected:
|
||||||
|
void AddButton(u32 id, const char *text, float x, float y, float w, float h);
|
||||||
|
void SetButtonSelected(u32 id, bool selected);
|
||||||
|
void DeselectAllButtons();
|
||||||
|
void SetButtonEnabled(u32 id, bool enabled);
|
||||||
|
|
||||||
|
Button *GetButton(u32 id);
|
||||||
|
Button *GetSelectedButton();
|
||||||
|
Button *GetClosestButtonToSelection(Direction direction);
|
||||||
|
Button *GetTouchedButton();
|
||||||
|
Button *GetActivatedButton();
|
||||||
|
|
||||||
|
void UpdateButtons();
|
||||||
|
void DrawButtons(NVGcontext *vg, u64 ns);
|
||||||
|
|
||||||
|
void LogText(const char *format, ...);
|
||||||
|
public:
|
||||||
|
Menu(std::shared_ptr<Menu> prev_menu) : m_buttons({}), m_prev_menu(prev_menu), m_log_buffer{} { /* ... */ }
|
||||||
|
|
||||||
|
std::shared_ptr<Menu> GetPrevMenu();
|
||||||
|
virtual void Update(u64 ns) = 0;
|
||||||
|
virtual void Draw(NVGcontext *vg, u64 ns) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MainMenu : public Menu {
|
||||||
|
private:
|
||||||
|
static constexpr u32 InstallButtonId = 0;
|
||||||
|
static constexpr u32 ExitButtonId = 1;
|
||||||
|
|
||||||
|
static constexpr float WindowWidth = 400.0f;
|
||||||
|
static constexpr float WindowHeight = 240.0f;
|
||||||
|
static constexpr float TitleGap = 90.0f;
|
||||||
|
static constexpr float ButtonHorizontalPadding = 20.0f;
|
||||||
|
static constexpr float ButtonHeight = 60.0f;
|
||||||
|
static constexpr float ButtonVerticalGap = 10.0f;
|
||||||
|
public:
|
||||||
|
MainMenu();
|
||||||
|
|
||||||
|
virtual void Update(u64 ns) override;
|
||||||
|
virtual void Draw(NVGcontext *vg, u64 ns) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FileMenu : public Menu {
|
||||||
|
private:
|
||||||
|
struct FileEntry {
|
||||||
|
char name[FS_MAX_PATH];
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
static constexpr size_t MaxFileRows = 11;
|
||||||
|
|
||||||
|
static constexpr float WindowWidth = 1200.0f;
|
||||||
|
static constexpr float WindowHeight = 680.0f;
|
||||||
|
static constexpr float TitleGap = 90.0f;
|
||||||
|
static constexpr float TextBackgroundOffset = 20.0f;
|
||||||
|
static constexpr float FileRowHeight = 40.0f;
|
||||||
|
static constexpr float FileRowGap = 10.0f;
|
||||||
|
static constexpr float FileRowHorizontalInset = 10.0f;
|
||||||
|
static constexpr float FileListHeight = MaxFileRows * (FileRowHeight + FileRowGap);
|
||||||
|
private:
|
||||||
|
char m_root[FS_MAX_PATH];
|
||||||
|
std::vector<FileEntry> m_file_entries;
|
||||||
|
u32 m_current_index;
|
||||||
|
float m_scroll_offset;
|
||||||
|
float m_touch_start_scroll_offset;
|
||||||
|
bool m_touch_finalize_selection;
|
||||||
|
|
||||||
|
Result PopulateFileEntries();
|
||||||
|
bool IsSelectionVisible();
|
||||||
|
void ScrollToSelection();
|
||||||
|
bool IsEntryTouched(u32 i);
|
||||||
|
void UpdateTouches();
|
||||||
|
void FinalizeSelection();
|
||||||
|
public:
|
||||||
|
FileMenu(std::shared_ptr<Menu> prev_menu, const char *root);
|
||||||
|
|
||||||
|
virtual void Update(u64 ns) override;
|
||||||
|
virtual void Draw(NVGcontext *vg, u64 ns) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ValidateUpdateMenu : public Menu {
|
||||||
|
private:
|
||||||
|
static constexpr u32 BackButtonId = 0;
|
||||||
|
static constexpr u32 ContinueButtonId = 1;
|
||||||
|
|
||||||
|
static constexpr float WindowWidth = 600.0f;
|
||||||
|
static constexpr float WindowHeight = 600.0f;
|
||||||
|
static constexpr float TitleGap = 90.0f;
|
||||||
|
static constexpr float BottomGap = 20.0f;
|
||||||
|
static constexpr float HorizontalGap = 20.0f;
|
||||||
|
static constexpr float TextAreaHeight = 410.0f;
|
||||||
|
static constexpr float TextHorizontalInset = 6.0f;
|
||||||
|
static constexpr float TextVerticalInset = 6.0f;
|
||||||
|
static constexpr float ButtonHeight = 60.0f;
|
||||||
|
static constexpr float ButtonHorizontalGap = 10.0f;
|
||||||
|
static constexpr float ButtonWidth = (WindowWidth - HorizontalGap * 2.0f) / 2.0f - ButtonHorizontalGap;
|
||||||
|
private:
|
||||||
|
AmsSuUpdateInformation m_update_info;
|
||||||
|
AmsSuUpdateValidationInfo m_validation_info;
|
||||||
|
bool m_has_drawn;
|
||||||
|
bool m_has_info;
|
||||||
|
bool m_has_validated;
|
||||||
|
|
||||||
|
Result GetUpdateInformation();
|
||||||
|
void ValidateUpdate();
|
||||||
|
public:
|
||||||
|
ValidateUpdateMenu(std::shared_ptr<Menu> prev_menu);
|
||||||
|
|
||||||
|
virtual void Update(u64 ns) override;
|
||||||
|
virtual void Draw(NVGcontext *vg, u64 ns) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ChooseExfatMenu : public Menu {
|
||||||
|
private:
|
||||||
|
static constexpr u32 Fat32ButtonId = 0;
|
||||||
|
static constexpr u32 ExFatButtonId = 1;
|
||||||
|
|
||||||
|
static constexpr float WindowWidth = 600.0f;
|
||||||
|
static constexpr float WindowHeight = 180.0f;
|
||||||
|
static constexpr float TitleGap = 90.0f;
|
||||||
|
static constexpr float ButtonHeight = 60.0f;
|
||||||
|
static constexpr float ButtonHorizontalInset = 20.0f;
|
||||||
|
static constexpr float ButtonHorizontalGap = 10.0f;
|
||||||
|
static constexpr float ButtonWidth = (WindowWidth - ButtonHorizontalInset * 2.0f) / 2.0f - ButtonHorizontalGap;
|
||||||
|
public:
|
||||||
|
ChooseExfatMenu(std::shared_ptr<Menu> prev_menu);
|
||||||
|
|
||||||
|
virtual void Update(u64 ns) override;
|
||||||
|
virtual void Draw(NVGcontext *vg, u64 ns) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class InstallUpdateMenu : public Menu {
|
||||||
|
private:
|
||||||
|
enum class InstallState {
|
||||||
|
NeedsDraw,
|
||||||
|
NeedsSetup,
|
||||||
|
NeedsPrepare,
|
||||||
|
AwaitingPrepare,
|
||||||
|
NeedsApply,
|
||||||
|
AwaitingReboot,
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
static constexpr u32 ShutdownButtonId = 0;
|
||||||
|
static constexpr u32 RebootButtonId = 1;
|
||||||
|
|
||||||
|
static constexpr float WindowWidth = 600.0f;
|
||||||
|
static constexpr float WindowHeight = 600.0f;
|
||||||
|
static constexpr float TitleGap = 120.0f;
|
||||||
|
static constexpr float BottomGap = 20.0f;
|
||||||
|
static constexpr float HorizontalGap = 20.0f;
|
||||||
|
static constexpr float ProgressTextHeight = 20.0f;
|
||||||
|
static constexpr float ProgressBarHeight = 30.0f;
|
||||||
|
static constexpr float VerticalGap = 10.0f;
|
||||||
|
static constexpr float TextAreaHeight = 320.0f;
|
||||||
|
static constexpr float TextHorizontalInset = 6.0f;
|
||||||
|
static constexpr float TextVerticalInset = 6.0f;
|
||||||
|
static constexpr float ButtonHeight = 60.0f;
|
||||||
|
static constexpr float ButtonHorizontalGap = 10.0f;
|
||||||
|
static constexpr float ButtonWidth = (WindowWidth - HorizontalGap * 2.0f) / 2.0f - ButtonHorizontalGap;
|
||||||
|
|
||||||
|
static constexpr size_t UpdateTaskBufferSize = 0x100000;
|
||||||
|
private:
|
||||||
|
InstallState m_install_state;
|
||||||
|
AsyncResult m_prepare_result;
|
||||||
|
float m_progress_percent;
|
||||||
|
|
||||||
|
void MarkForReboot();
|
||||||
|
Result TransitionUpdateState();
|
||||||
|
public:
|
||||||
|
InstallUpdateMenu(std::shared_ptr<Menu> prev_menu);
|
||||||
|
|
||||||
|
virtual void Update(u64 ns) override;
|
||||||
|
virtual void Draw(NVGcontext *vg, u64 ns) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
void InitializeMenu(u32 screen_width, u32 screen_height);
|
||||||
|
void UpdateMenu(u64 ns);
|
||||||
|
void RenderMenu(NVGcontext *vg, u64 ns);
|
||||||
|
bool IsExitRequested();
|
||||||
|
|
||||||
|
}
|
192
troposphere/daybreak/source/ui_util.cpp
Normal file
192
troposphere/daybreak/source/ui_util.cpp
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Adubbz
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include "ui_util.hpp"
|
||||||
|
#include <cstdio>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
namespace dbk {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr const char *SwitchStandardFont = "switch-standard";
|
||||||
|
constexpr float WindowCornerRadius = 20.0f;
|
||||||
|
constexpr float TextAreaCornerRadius = 10.0f;
|
||||||
|
constexpr float ButtonCornerRaidus = 3.0f;
|
||||||
|
|
||||||
|
NVGcolor GetSelectionRGB2(u64 ns) {
|
||||||
|
/* Calculate the rgb values for the breathing colour effect. */
|
||||||
|
const double t = static_cast<double>(ns) / 1'000'000'000.0d;
|
||||||
|
const float d = -0.5 * cos(3.0f*t) + 0.5f;
|
||||||
|
const int r2 = 83 + (float)(144 - 83) * (d * 0.7f + 0.3f);
|
||||||
|
const int g2 = 71 + (float)(185 - 71) * (d * 0.7f + 0.3f);
|
||||||
|
const int b2 = 185 + (float)(217 - 185) * (d * 0.7f + 0.3f);
|
||||||
|
return nvgRGB(r2, g2, b2);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawStar(NVGcontext *vg, float x, float y, float width) {
|
||||||
|
nvgBeginPath(vg);
|
||||||
|
nvgEllipse(vg, x, y, width, width * 3.0f);
|
||||||
|
nvgEllipse(vg, x, y, width * 3.0f, width);
|
||||||
|
nvgFillColor(vg, nvgRGB(65, 71, 115));
|
||||||
|
nvgFill(vg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawBackground(NVGcontext *vg, float w, float h) {
|
||||||
|
/* Draw the background gradient. */
|
||||||
|
const NVGpaint bg_paint = nvgLinearGradient(vg, w / 2.0f, 0, w / 2.0f, h + 20.0f, nvgRGB(20, 24, 50), nvgRGB(46, 57, 127));
|
||||||
|
nvgBeginPath(vg);
|
||||||
|
nvgRect(vg, 0, 0, w, h);
|
||||||
|
nvgFillPaint(vg, bg_paint);
|
||||||
|
nvgFill(vg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawWindow(NVGcontext *vg, const char *title, float x, float y, float w, float h) {
|
||||||
|
/* Draw the window background. */
|
||||||
|
const NVGpaint window_bg_paint = nvgLinearGradient(vg, x + w / 2.0f, y, x + w / 2.0f, y + h + h / 4.0f, nvgRGB(255, 255, 255), nvgRGB(188, 214, 234));
|
||||||
|
nvgBeginPath(vg);
|
||||||
|
nvgRoundedRect(vg, x, y, w, h, WindowCornerRadius);
|
||||||
|
nvgFillPaint(vg, window_bg_paint);
|
||||||
|
nvgFill(vg);
|
||||||
|
|
||||||
|
/* Draw the shadow surrounding the window. */
|
||||||
|
NVGpaint shadowPaint = nvgBoxGradient(vg, x, y + 2, w, h, WindowCornerRadius * 2, 10, nvgRGBA(0, 0, 0, 128), nvgRGBA(0, 0, 0, 0));
|
||||||
|
nvgBeginPath(vg);
|
||||||
|
nvgRect(vg, x - 10, y - 10, w + 20, h + 30);
|
||||||
|
nvgRoundedRect(vg, x, y, w, h, WindowCornerRadius);
|
||||||
|
nvgPathWinding(vg, NVG_HOLE);
|
||||||
|
nvgFillPaint(vg, shadowPaint);
|
||||||
|
nvgFill(vg);
|
||||||
|
|
||||||
|
/* Setup the font. */
|
||||||
|
nvgFontSize(vg, 32.0f);
|
||||||
|
nvgFontFace(vg, SwitchStandardFont);
|
||||||
|
nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
|
||||||
|
nvgFillColor(vg, nvgRGB(0, 0, 0));
|
||||||
|
|
||||||
|
/* Draw the title. */
|
||||||
|
const float tw = nvgTextBounds(vg, 0, 0, title, nullptr, nullptr);
|
||||||
|
nvgText(vg, x + w * 0.5f - tw * 0.5f, y + 40.0f, title, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawButton(NVGcontext *vg, const char *text, float x, float y, float w, float h, ButtonStyle style, u64 ns) {
|
||||||
|
/* Fill the background if selected. */
|
||||||
|
if (style == ButtonStyle::StandardSelected || style == ButtonStyle::FileSelectSelected) {
|
||||||
|
NVGpaint bg_paint = nvgLinearGradient(vg, x, y + h / 2.0f, x + w, y + h / 2.0f, nvgRGB(83, 71, 185), GetSelectionRGB2(ns));
|
||||||
|
nvgBeginPath(vg);
|
||||||
|
nvgRoundedRect(vg, x, y, w, h, ButtonCornerRaidus);
|
||||||
|
nvgFillPaint(vg, bg_paint);
|
||||||
|
nvgFill(vg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draw the shadow surrounding the button. */
|
||||||
|
if (style == ButtonStyle::Standard || style == ButtonStyle::StandardSelected || style == ButtonStyle::StandardDisabled || style == ButtonStyle::FileSelectSelected) {
|
||||||
|
const unsigned char shadow_color = style == ButtonStyle::Standard ? 128 : 64;
|
||||||
|
NVGpaint shadow_paint = nvgBoxGradient(vg, x, y, w, h, ButtonCornerRaidus, 5, nvgRGBA(0, 0, 0, shadow_color), nvgRGBA(0, 0, 0, 0));
|
||||||
|
nvgBeginPath(vg);
|
||||||
|
nvgRect(vg, x - 10, y - 10, w + 20, h + 30);
|
||||||
|
nvgRoundedRect(vg, x, y, w, h, ButtonCornerRaidus);
|
||||||
|
nvgPathWinding(vg, NVG_HOLE);
|
||||||
|
nvgFillPaint(vg, shadow_paint);
|
||||||
|
nvgFill(vg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup the font. */
|
||||||
|
nvgFontSize(vg, 20.0f);
|
||||||
|
nvgFontFace(vg, SwitchStandardFont);
|
||||||
|
nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
|
||||||
|
|
||||||
|
/* Set the text colour. */
|
||||||
|
if (style == ButtonStyle::StandardSelected || style == ButtonStyle::FileSelectSelected) {
|
||||||
|
nvgFillColor(vg, nvgRGB(255, 255, 255));
|
||||||
|
} else {
|
||||||
|
const unsigned char alpha = style == ButtonStyle::StandardDisabled ? 64 : 255;
|
||||||
|
nvgFillColor(vg, nvgRGBA(0, 0, 0, alpha));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draw the button text. */
|
||||||
|
const float tw = nvgTextBounds(vg, 0, 0, text, nullptr, nullptr);
|
||||||
|
|
||||||
|
if (style == ButtonStyle::Standard || style == ButtonStyle::StandardSelected || style == ButtonStyle::StandardDisabled) {
|
||||||
|
nvgText(vg, x + w * 0.5f - tw * 0.5f, y + h * 0.5f, text, nullptr);
|
||||||
|
} else {
|
||||||
|
nvgText(vg, x + 10.0f, y + h * 0.5f, text, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawTextBackground(NVGcontext *vg, float x, float y, float w, float h) {
|
||||||
|
nvgBeginPath(vg);
|
||||||
|
nvgRoundedRect(vg, x, y, w, h, TextAreaCornerRadius);
|
||||||
|
nvgFillColor(vg, nvgRGBA(0, 0, 0, 16));
|
||||||
|
nvgFill(vg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawProgressText(NVGcontext *vg, float x, float y, float progress) {
|
||||||
|
char progress_text[32] = {};
|
||||||
|
snprintf(progress_text, sizeof(progress_text)-1, "%d%% complete", static_cast<int>(progress * 100.0f));
|
||||||
|
|
||||||
|
nvgFontSize(vg, 24.0f);
|
||||||
|
nvgFontFace(vg, SwitchStandardFont);
|
||||||
|
nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
|
||||||
|
nvgFillColor(vg, nvgRGB(0, 0, 0));
|
||||||
|
nvgText(vg, x, y, progress_text, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawProgressBar(NVGcontext *vg, float x, float y, float w, float h, float progress) {
|
||||||
|
/* Draw the progress bar background. */
|
||||||
|
nvgBeginPath(vg);
|
||||||
|
nvgRoundedRect(vg, x, y, w, h, WindowCornerRadius);
|
||||||
|
nvgFillColor(vg, nvgRGBA(0, 0, 0, 128));
|
||||||
|
nvgFill(vg);
|
||||||
|
|
||||||
|
/* Draw the progress bar fill. */
|
||||||
|
if (progress > 0.0f) {
|
||||||
|
NVGpaint progress_fill_paint = nvgLinearGradient(vg, x, y + 0.5f * h, x + w, y + 0.5f * h, nvgRGB(83, 71, 185), nvgRGB(144, 185, 217));
|
||||||
|
nvgBeginPath(vg);
|
||||||
|
nvgRoundedRect(vg, x, y, WindowCornerRadius + (w - WindowCornerRadius) * progress, h, WindowCornerRadius);
|
||||||
|
nvgFillPaint(vg, progress_fill_paint);
|
||||||
|
nvgFill(vg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawTextBlock(NVGcontext *vg, const char *text, float x, float y, float w, float h) {
|
||||||
|
/* Save state and scissor. */
|
||||||
|
nvgSave(vg);
|
||||||
|
nvgScissor(vg, x, y, w, h);
|
||||||
|
|
||||||
|
/* Configure the text. */
|
||||||
|
nvgFontSize(vg, 18.0f);
|
||||||
|
nvgFontFace(vg, SwitchStandardFont);
|
||||||
|
nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
|
||||||
|
nvgFillColor(vg, nvgRGB(0, 0, 0));
|
||||||
|
|
||||||
|
/* Determine the bounds of the text box. */
|
||||||
|
float bounds[4];
|
||||||
|
nvgTextBoxBounds(vg, 0, 0, w, text, nullptr, bounds);
|
||||||
|
|
||||||
|
/* Adjust the y to only show the last part of the text that fits. */
|
||||||
|
float y_adjustment = 0.0f;
|
||||||
|
if (bounds[3] > h) {
|
||||||
|
y_adjustment = bounds[3] - h;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draw the text box and restore state. */
|
||||||
|
nvgTextBox(vg, x, y - y_adjustment, w, text, nullptr);
|
||||||
|
nvgRestore(vg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
39
troposphere/daybreak/source/ui_util.hpp
Normal file
39
troposphere/daybreak/source/ui_util.hpp
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 Adubbz
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <nanovg.h>
|
||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
namespace dbk {
|
||||||
|
|
||||||
|
enum class ButtonStyle {
|
||||||
|
Standard,
|
||||||
|
StandardSelected,
|
||||||
|
StandardDisabled,
|
||||||
|
FileSelect,
|
||||||
|
FileSelectSelected,
|
||||||
|
};
|
||||||
|
|
||||||
|
void DrawStar(NVGcontext *vg, float x, float y, float width);
|
||||||
|
void DrawBackground(NVGcontext *vg, float w, float h);
|
||||||
|
void DrawWindow(NVGcontext *vg, const char *title, float x, float y, float w, float h);
|
||||||
|
void DrawButton(NVGcontext *vg, const char *text, float x, float y, float w, float h, ButtonStyle style, u64 ns);
|
||||||
|
void DrawTextBackground(NVGcontext *vg, float x, float y, float w, float h);
|
||||||
|
void DrawProgressText(NVGcontext *vg, float x, float y, float progress);
|
||||||
|
void DrawProgressBar(NVGcontext *vg, float x, float y, float w, float h, float progress);
|
||||||
|
void DrawTextBlock(NVGcontext *vg, const char *text, float x, float y, float w, float h);
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue