refactor(webgl): Move WebGL GAL from test harness to KiCad source tree

Migrate WebGL GAL implementation from tests/gal-regression/wasm/webgl/
to kicad/common/gal/webgl/ and kicad/include/gal/webgl/.

This integrates the WebGL GAL properly into KiCad's build system:
- Update test Makefile to use sources from kicad/ instead of local copies
- Update build scripts for new source locations
- Add test-gal-webgl.sh script for running WebGL regression tests
- Update Docker to Emscripten 4.0.22

The WebGL GAL passes all 28 regression tests (matching baseline).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Viktor Vaczi 2026-01-10 13:15:58 +01:00
commit 37d973f064
50 changed files with 403 additions and 28977 deletions

1
.gitignore vendored
View file

@ -55,3 +55,4 @@ wxwidgets-clean/
*.log
*.tmp
output/
*.d

View file

@ -1,5 +1,5 @@
# Use ARM64-native image for Apple Silicon (M1/M2/M3/M4)
FROM emscripten/emsdk:4.0.2-arm64
FROM emscripten/emsdk:4.0.22-arm64
# Install build tools required for KiCad WASM build
RUN apt-get update && apt-get install -y \

2
kicad

@ -1 +1 @@
Subproject commit 0e07ba164cb533c60d2904ca8c3a329182dc843a
Subproject commit 1b5bb125d2fa94944641bf73c1d51057042292b5

View file

@ -8,6 +8,11 @@
# Uses a Makefile with direct em++ calls (like build-wasm-test.sh)
# to avoid emcmake Python 3.10+ requirement.
#
# Usage:
# ./scripts/build-gal-webgl-test.sh # Clean build (default)
# ./scripts/build-gal-webgl-test.sh --no-clean # Incremental build
# ./scripts/build-gal-webgl-test.sh --debug # Debug build with source maps
#
# Redirect all output to a log file (re-execs script with redirection)
source "$(dirname "$0")/common/logging.sh"
@ -37,18 +42,19 @@ fi
echo " Emscripten: $(em++ --version 2>&1 | head -1)"
# Parse arguments
# Default: clean build to avoid stale object file issues (header deps not tracked in old builds)
DEBUG_BUILD=0
CLEAN_BUILD=0
CLEAN_BUILD=1
for arg in "$@"; do
if [ "$arg" = "--debug" ]; then
DEBUG_BUILD=1
elif [ "$arg" = "--clean" ]; then
CLEAN_BUILD=1
elif [ "$arg" = "--no-clean" ]; then
CLEAN_BUILD=0
fi
done
# Build using Makefile
# Build using Makefile (compiles WebGL sources directly from kicad/)
cd "$TEST_DIR"
if [ "$CLEAN_BUILD" = "1" ]; then
@ -62,6 +68,7 @@ fi
rm -f "$OUTPUT_DIR"/*.js "$OUTPUT_DIR"/*.wasm 2>/dev/null || true
# Generate shaders (converts GLSL 1.20 to GLSL ES 3.00)
# TODO: Eventually these should come from KiCad's build
echo ""
echo "Generating WebGL shaders..."
python3 generate_shaders.py

View file

@ -239,7 +239,7 @@ emcmake cmake "${KICAD_DIR}" \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
-DCMAKE_CXX_FLAGS="${EXTRA_FLAGS} -pthread -sUSE_ZLIB=1 -DKICAD_USE_PLATFORM_WASM=1 -I${SYSROOT}/include -I${STUBS_DIR}" \
-DCMAKE_C_FLAGS="${EXTRA_FLAGS} -pthread -sUSE_ZLIB=1 -I${SYSROOT}/include -I${STUBS_DIR}" \
-DCMAKE_EXE_LINKER_FLAGS="${LINKER_DEBUG_FLAGS} -pthread -sUSE_ZLIB=1 -sASYNCIFY=1 -sDYNCALLS=1 -sASYNCIFY_STACK_SIZE=65536 -sUSE_PTHREADS=1 -sPTHREAD_POOL_SIZE='navigator.hardwareConcurrency' -sPTHREAD_POOL_SIZE_STRICT=0 -sALLOW_MEMORY_GROWTH=1 -sINITIAL_MEMORY=256MB -sMAXIMUM_MEMORY=4GB -sLEGACY_GL_EMULATION -sMAX_WEBGL_VERSION=2 -sEXPORTED_RUNTIME_METHODS=['ccall','cwrap','UTF8ToString','stringToUTF8','lengthBytesUTF8','dynCall'] -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=['\$dynCall'] --bind -L${SYSROOT}/lib ${STUBS_BUILD}/libgit2_stub.a ${STUBS_BUILD}/libcurl_stub.a ${STUBS_BUILD}/libpcbnew_scripting_stub.a ${STUBS_BUILD}/libnng_stub.a ${STUBS_BUILD}/pcbnew_embind.o" \
-DCMAKE_EXE_LINKER_FLAGS="${LINKER_DEBUG_FLAGS} -pthread -sUSE_ZLIB=1 -sASYNCIFY=1 -sDYNCALLS=1 -sASYNCIFY_STACK_SIZE=65536 -sUSE_PTHREADS=1 -sPTHREAD_POOL_SIZE='navigator.hardwareConcurrency' -sPTHREAD_POOL_SIZE_STRICT=0 -sALLOW_MEMORY_GROWTH=1 -sINITIAL_MEMORY=256MB -sMAXIMUM_MEMORY=4GB -sMAX_WEBGL_VERSION=2 -sFULL_ES3=1 -sEXPORTED_RUNTIME_METHODS=['ccall','cwrap','UTF8ToString','stringToUTF8','lengthBytesUTF8','dynCall'] -sDEFAULT_LIBRARY_FUNCS_TO_INCLUDE=['\$dynCall'] --bind -L${SYSROOT}/lib ${STUBS_BUILD}/libgit2_stub.a ${STUBS_BUILD}/libcurl_stub.a ${STUBS_BUILD}/libpcbnew_scripting_stub.a ${STUBS_BUILD}/libnng_stub.a ${STUBS_BUILD}/pcbnew_embind.o" \
-DCMAKE_PREFIX_PATH="${SYSROOT};${WX_BUILD}" \
-DwxWidgets_CONFIG_EXECUTABLE="${WX_BUILD}/wx-config" \
\

View file

@ -320,24 +320,18 @@ build_webgl() {
log_header "Building WebGL Test"
if [ ! -f "$SCRIPT_DIR/build-gal-webgl-test.sh" ]; then
log_step "WebGL build script not found (Phase 2)"
return 0
fi
# Check if Emscripten is available
if ! command -v emcmake &> /dev/null; then
log_step "Emscripten not available - WebGL build requires emsdk or Docker"
log_step "Skipping WebGL build (run inside Docker or activate emsdk first)"
log_step "WebGL build script not found"
return 0
fi
# Delegate to build-gal-webgl-test.sh (handles Emscripten check, clean build, etc.)
log_step "Running scripts/build-gal-webgl-test.sh..."
if "$SCRIPT_DIR/build-gal-webgl-test.sh"; then
log_success "WebGL build succeeded"
else
log_error "WebGL build failed (Emscripten may need Python 3.10+)"
log_step "Try running inside Docker or with a compatible emsdk"
log_error "WebGL build failed"
log_step "Check if KiCad is built first: docker/build.sh"
return 0 # Don't fail the whole test, just skip WebGL
fi
}

340
scripts/test-gal-webgl.sh Executable file
View file

@ -0,0 +1,340 @@
#!/bin/bash
# GAL WebGL Regression Test - Monitoring Script
# ==============================================
# Single entry point for WebGL GAL testing.
# Always does a clean build, then runs tests and compares against baseline.
#
# Purpose: Detect regressions during WebGL GAL migration to KiCad.
# Strategy: Lock in current rendering behavior as baseline, then monitor
# for any changes as we integrate WebGL GAL into KiCad build.
#
# Usage:
# ./scripts/test-gal-webgl.sh # Clean build and test (default)
# ./scripts/test-gal-webgl.sh -v # Verbose output
# Redirect all output to a log file (re-execs script with redirection)
source "$(dirname "$0")/common/logging.sh"
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
# Directories
GAL_REGRESSION_DIR="$PROJECT_ROOT/tests/gal-regression"
BASELINE_WEBGL_DIR="$GAL_REGRESSION_DIR/baseline-webgl"
OUTPUT_DIR="$GAL_REGRESSION_DIR/output"
WEBGL_OUTPUT_DIR="$OUTPUT_DIR/webgl"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Parse arguments
VERBOSE=""
for arg in "$@"; do
case $arg in
-v|--verbose)
VERBOSE="-v"
;;
esac
done
# ============================================================================
# Helper Functions
# ============================================================================
log_header() {
echo ""
echo -e "${BLUE}════════════════════════════════════════════════════════════════${NC}"
echo -e "${BLUE} $1${NC}"
echo -e "${BLUE}════════════════════════════════════════════════════════════════${NC}"
}
log_step() {
echo -e "${YELLOW}>>> $1${NC}"
}
log_success() {
echo -e "${GREEN}$1${NC}"
}
log_error() {
echo -e "${RED}$1${NC}"
}
# Compare WebGL output against baseline-webgl
# Usage: compare_webgl_screenshots [threshold]
# Returns 0 if match within threshold, 1 if different
compare_webgl_screenshots() {
local threshold="${1:-1.0}" # Default 1% difference allowed
echo ""
echo "Comparing WebGL output against baseline:"
echo " Baseline: $BASELINE_WEBGL_DIR"
echo " Current: $WEBGL_OUTPUT_DIR"
echo " Threshold: ${threshold}% pixel difference"
echo ""
if [ ! -d "$BASELINE_WEBGL_DIR" ]; then
log_error "Baseline directory not found: $BASELINE_WEBGL_DIR"
log_step "Run full test suite first to create baseline: ./scripts/test-gal-regression.sh"
return 1
fi
if [ ! -d "$WEBGL_OUTPUT_DIR" ]; then
log_error "WebGL output directory not found: $WEBGL_OUTPUT_DIR"
return 1
fi
# Check for ImageMagick
if ! command -v compare &> /dev/null; then
log_error "ImageMagick 'compare' command not found. Install with: brew install imagemagick"
return 1
fi
local total=0
local matching=0
local different=0
local missing=0
# Create temp directory for normalized images
local tmpdir=$(mktemp -d)
trap "rm -rf '$tmpdir'" EXIT
# Files to exclude from comparison (documented as broken/dead code)
local excluded_files="gal-transform-api.png"
# Compare all baseline screenshots
for baseline in "$BASELINE_WEBGL_DIR"/*.png; do
[ -e "$baseline" ] || continue # Handle no matches
local filename=$(basename "$baseline")
# Skip excluded files
if echo "$excluded_files" | grep -q "$filename"; then
if [ -n "$VERBOSE" ]; then
echo " SKIPPED: $filename (excluded - see README.md)"
fi
continue
fi
local current="$WEBGL_OUTPUT_DIR/$filename"
total=$((total + 1))
if [ ! -f "$current" ]; then
echo " MISSING: $filename"
missing=$((missing + 1))
continue
fi
# Get dimensions
local baseline_dims=$(identify -format "%wx%h" "$baseline" 2>/dev/null)
local current_dims=$(identify -format "%wx%h" "$current" 2>/dev/null)
# If dimensions differ, resize current to match baseline
local compare_file="$current"
if [ "$baseline_dims" != "$current_dims" ]; then
compare_file="$tmpdir/resized_$filename"
convert "$current" -resize "$baseline_dims!" "$compare_file" 2>/dev/null
if [ -n "$VERBOSE" ]; then
echo " RESIZED: $filename ($current_dims -> $baseline_dims)"
fi
fi
# Normalize both images to TrueColor RGB for consistent comparison
local baseline_normalized="$tmpdir/baseline_$filename"
local current_normalized="$tmpdir/current_$filename"
convert "$baseline" -flatten -colorspace sRGB -type TrueColor "$baseline_normalized" 2>/dev/null
convert "$compare_file" -flatten -colorspace sRGB -type TrueColor "$current_normalized" 2>/dev/null
# Compare with fuzz factor (allows small pixel differences from anti-aliasing)
# Use AE (Absolute Error) metric - counts differing pixels
local compare_output=$(compare -metric AE -fuzz 2% "$baseline_normalized" "$current_normalized" null: 2>&1 || true)
local diff_pixels=$(echo "$compare_output" | awk '{print $1}')
# Handle scientific notation (e.g., 1.92e+06)
if [[ "$diff_pixels" =~ [eE] ]]; then
diff_pixels=$(printf "%.0f" "$diff_pixels")
fi
# Handle error cases (non-numeric output)
if ! [[ "$diff_pixels" =~ ^[0-9]+\.?[0-9]*$ ]]; then
echo " ERROR: $filename (comparison failed: $compare_output)"
different=$((different + 1))
continue
fi
# Calculate percentage
local total_pixels=$(identify -format "%[fx:w*h]" "$baseline_normalized" 2>/dev/null)
# Handle scientific notation
if [[ "$total_pixels" =~ [eE] ]]; then
total_pixels=$(printf "%.0f" "$total_pixels")
fi
if [ -z "$total_pixels" ] || [ "$total_pixels" = "0" ]; then
total_pixels=1 # Avoid division by zero
fi
# Use awk for floating point arithmetic
local diff_pct=$(awk "BEGIN {printf \"%.4f\", ($diff_pixels * 100.0) / $total_pixels}")
# Compare against threshold
local is_match=$(awk "BEGIN {print ($diff_pct < $threshold) ? 1 : 0}")
if [ "$is_match" -eq 1 ]; then
matching=$((matching + 1))
if [ -n "$VERBOSE" ] || [ "$diff_pixels" -gt 0 ]; then
echo " MATCH: $filename (${diff_pct}% different)"
fi
else
different=$((different + 1))
echo " DIFFERENT: $filename (${diff_pct}% different, threshold: ${threshold}%)"
# Show diagnostic info for failing scenario
if [ -n "$VERBOSE" ]; then
echo " Content bounds:"
local baseline_bounds=$(magick "$baseline" -flatten -fuzz 1% -trim -format "%w x %h at %O" info: 2>/dev/null || echo "N/A")
local current_bounds=$(magick "$current" -fuzz 1% -trim -format "%w x %h at %O" info: 2>/dev/null || echo "N/A")
echo " Baseline: $baseline_bounds"
echo " Current: $current_bounds"
fi
fi
done
# Check for extra files
local extra=0
for current in "$WEBGL_OUTPUT_DIR"/*.png; do
[ -e "$current" ] || continue
local filename=$(basename "$current")
local baseline="$BASELINE_WEBGL_DIR/$filename"
if [ ! -f "$baseline" ]; then
extra=$((extra + 1))
echo " EXTRA: $filename (not in baseline)"
fi
done
echo ""
echo " Results: $matching/$total matching, $different different, $missing missing, $extra extra"
if [ "$different" -gt 0 ] || [ "$missing" -gt 0 ]; then
log_error "WebGL vs Baseline: FAILED"
echo ""
echo "MIGRATION REGRESSION DETECTED!"
echo "WebGL output has changed from baseline. This indicates the migration"
echo "has altered rendering behavior. Please investigate before proceeding."
echo ""
echo "To update baseline (if changes are intentional):"
echo " cp $WEBGL_OUTPUT_DIR/*.png $BASELINE_WEBGL_DIR/"
echo " git add $BASELINE_WEBGL_DIR/"
echo " git commit -m 'Update WebGL baseline after migration'"
return 1
else
log_success "WebGL vs Baseline: PASSED"
return 0
fi
}
# ============================================================================
# Build Function
# ============================================================================
build_webgl() {
log_header "Building WebGL Test"
if [ ! -f "$SCRIPT_DIR/build-gal-webgl-test.sh" ]; then
log_error "WebGL build script not found: $SCRIPT_DIR/build-gal-webgl-test.sh"
exit 1
fi
# Check if Emscripten is available
if ! command -v emcmake &> /dev/null; then
log_error "Emscripten not available - WebGL build requires emsdk or Docker"
log_step "Activate emsdk first or run inside Docker container"
exit 1
fi
log_step "Running scripts/build-gal-webgl-test.sh..."
if "$SCRIPT_DIR/build-gal-webgl-test.sh"; then
log_success "WebGL build succeeded"
else
log_error "WebGL build failed"
exit 1
fi
}
# ============================================================================
# Run Function
# ============================================================================
run_webgl() {
log_header "Running WebGL Test"
local spec_file="$PROJECT_ROOT/tests/e2e/gal-webgl.spec.ts"
if [ ! -f "$spec_file" ]; then
log_error "WebGL test spec not found: $spec_file"
exit 1
fi
# Create output directory
mkdir -p "$WEBGL_OUTPUT_DIR"
log_step "Running Playwright test..."
cd "$PROJECT_ROOT/tests"
if npx playwright test gal-webgl.spec.ts; then
local count=$(ls -1 "$WEBGL_OUTPUT_DIR"/*.png 2>/dev/null | wc -l | tr -d ' ')
log_success "WebGL test completed: $count screenshots generated"
else
log_error "WebGL test failed"
exit 1
fi
cd "$PROJECT_ROOT"
}
# ============================================================================
# Main
# ============================================================================
log_header "GAL WebGL Regression Test (Monitoring)"
echo "Project Root: $PROJECT_ROOT"
echo "Baseline WebGL: $BASELINE_WEBGL_DIR"
echo "Current Output: $WEBGL_OUTPUT_DIR"
echo ""
echo "Purpose: Detect regressions during WebGL GAL migration to KiCad"
echo "Strategy: Compare current WebGL output against baseline-webgl"
# Build phase (always clean build to avoid stale object issues)
build_webgl
# Run phase
run_webgl
# Compare phase
log_header "Comparing WebGL vs Baseline"
if compare_webgl_screenshots; then
COMPARE_STATUS=0
else
COMPARE_STATUS=1
fi
# Final summary
log_header "Final Result"
if [ "$COMPARE_STATUS" -eq 0 ]; then
log_success "WebGL output matches baseline perfectly!"
echo ""
echo "Migration safety check PASSED. Rendering fidelity preserved."
exit 0
else
log_error "WebGL output differs from baseline!"
echo ""
echo "Migration safety check FAILED. Review changes before proceeding."
exit 1
fi

View file

@ -35,17 +35,18 @@ KICAD_INCLUDES = -I../native \
-Igenerated \
-I$(KICAD_ROOT)/include \
-I$(KICAD_ROOT)/include/gal \
-I$(KICAD_ROOT)/include/gal/opengl \
-I$(KICAD_ROOT)/include/gal/webgl \
-I$(KICAD_ROOT)/common/gal/webgl \
-I$(KICAD_ROOT)/common \
-I$(KICAD_ROOT)/libs/kimath/include \
-I$(KICAD_ROOT)/libs/kimath/glu_tess \
-I$(KICAD_ROOT)/libs/kiplatform/include \
-I$(KICAD_ROOT)/libs/core/include \
-I$(KICAD_ROOT)/thirdparty/glm \
-I$(KICAD_ROOT)/thirdparty/nlohmann_json \
-I$(KICAD_ROOT)/thirdparty/thread-pool \
-I$(KICAD_ROOT)/thirdparty/clipper2/Clipper2Lib/include \
-I$(SYSROOT)/include \
-Iwebgl
-I$(SYSROOT)/include
# Debug or Release build
ifdef DEBUG
@ -56,7 +57,9 @@ else
DEBUG_LDFLAGS =
endif
CXXFLAGS = $(OPT_FLAGS) $(WX_CXXFLAGS) $(KICAD_INCLUDES) -std=c++20
# -MMD -MP generates .d dependency files for header tracking
# This ensures header changes trigger rebuilds (prevents stale object issues)
CXXFLAGS = $(OPT_FLAGS) $(WX_CXXFLAGS) $(KICAD_INCLUDES) -std=c++20 -MMD -MP
# Emscripten flags
BASE_LDFLAGS = -sALLOW_MEMORY_GROWTH=1 \
@ -88,21 +91,14 @@ HTML_TEMPLATE = $(TOOLS_ROOT)/template.html
# Source files
MAIN_SRCS = gal_webgl_test.cpp wasm_stubs.cpp
# KiCad sources needed for linking (not in WEBGL_SRCS)
KICAD_SRCS = $(KICAD_ROOT)/common/gal/hidpi_gl_canvas.cpp \
$(KICAD_ROOT)/common/gal/graphics_abstraction_layer.cpp \
$(KICAD_ROOT)/common/gal/gal_display_options.cpp
# Test scenario sources (shared with native test)
# Include ALL scenario files - they should use the abstract GAL interface only.
# The harness decides whether to use OPENGL_GAL (native) or WEBGL_GAL (wasm).
SCENARIO_SRCS = ../scenarios/gal_test_scenarios.cpp \
$(wildcard ../scenarios/scenario_*.cpp)
# GLU tesselator - implemented using Mapbox earcut.hpp
GLU_SRCS = glu_tess_impl.cpp
# Generated shader sources (from generate_shaders.py)
# TODO: Eventually these should come from KiCad's build, but for now we generate them here
SHADER_SRCS = generated/glsl_kicad_frag.cpp \
generated/glsl_kicad_vert.cpp \
generated/glsl_smaa_base.cpp \
@ -114,26 +110,35 @@ SHADER_SRCS = generated/glsl_kicad_frag.cpp \
generated/glsl_smaa_pass_3_frag.cpp \
generated/glsl_smaa_pass_3_vert.cpp
# WebGL GAL sources (ported from OpenGL GAL)
WEBGL_SRCS = webgl/webgl_gal.cpp \
webgl/vertex_manager.cpp \
webgl/vertex_container.cpp \
webgl/vertex_item.cpp \
webgl/gpu_manager.cpp \
webgl/noncached_container.cpp \
webgl/cached_container.cpp \
webgl/cached_container_gpu.cpp \
webgl/cached_container_ram.cpp \
webgl/shader.cpp \
webgl/webgl_compositor.cpp \
webgl/fullscreen_quad.cpp \
webgl/utils.cpp \
webgl/gl_context_mgr.cpp \
webgl/gl_resources.cpp \
webgl/webgl_antialiasing.cpp
# KiCad GAL sources needed for linking (base GAL classes)
KICAD_GAL_SRCS = $(KICAD_ROOT)/common/gal/hidpi_gl_canvas.cpp \
$(KICAD_ROOT)/common/gal/graphics_abstraction_layer.cpp \
$(KICAD_ROOT)/common/gal/gal_display_options.cpp
SRCS = $(MAIN_SRCS) $(KICAD_SRCS) $(SCENARIO_SRCS) $(GLU_SRCS) $(SHADER_SRCS) $(WEBGL_SRCS)
# GLU tesselator - implemented using Mapbox earcut.hpp
GLU_SRCS = $(KICAD_ROOT)/libs/kimath/glu_tess/glu_tess_impl.cpp
# WebGL GAL sources (now in kicad/common/gal/webgl/)
WEBGL_SRCS = $(KICAD_ROOT)/common/gal/webgl/webgl_gal.cpp \
$(KICAD_ROOT)/common/gal/webgl/vertex_manager.cpp \
$(KICAD_ROOT)/common/gal/webgl/vertex_container.cpp \
$(KICAD_ROOT)/common/gal/webgl/vertex_item.cpp \
$(KICAD_ROOT)/common/gal/webgl/gpu_manager.cpp \
$(KICAD_ROOT)/common/gal/webgl/noncached_container.cpp \
$(KICAD_ROOT)/common/gal/webgl/cached_container.cpp \
$(KICAD_ROOT)/common/gal/webgl/cached_container_gpu.cpp \
$(KICAD_ROOT)/common/gal/webgl/cached_container_ram.cpp \
$(KICAD_ROOT)/common/gal/webgl/shader.cpp \
$(KICAD_ROOT)/common/gal/webgl/webgl_compositor.cpp \
$(KICAD_ROOT)/common/gal/webgl/fullscreen_quad.cpp \
$(KICAD_ROOT)/common/gal/webgl/utils.cpp \
$(KICAD_ROOT)/common/gal/webgl/gl_context_mgr.cpp \
$(KICAD_ROOT)/common/gal/webgl/gl_resources.cpp \
$(KICAD_ROOT)/common/gal/webgl/webgl_antialiasing.cpp
SRCS = $(MAIN_SRCS) $(KICAD_GAL_SRCS) $(SCENARIO_SRCS) $(GLU_SRCS) $(SHADER_SRCS) $(WEBGL_SRCS)
OBJS = $(SRCS:.cpp=.o)
DEPS = $(OBJS:.o=.d)
# Target
TARGET = $(OUTPUT_DIR)/gal_webgl_test.js
@ -146,14 +151,21 @@ $(OUTPUT_DIR):
%.o: %.cpp
$(CXX) -c $(CXXFLAGS) $< -o $@
# Link test harness (compiles WebGL sources directly from kicad/)
$(TARGET): $(OBJS)
@echo "Linking $(words $(OBJS)) objects..."
$(CXX) $(OBJS) $(LDFLAGS) --pre-js $(JS) -o $@
cp gal_webgl_test.html $(OUTPUT_DIR)/
clean:
rm -f $(OBJS)
rm -f $(OBJS) $(DEPS)
rm -f $(OUTPUT_DIR)/gal_webgl_test.js
rm -f $(OUTPUT_DIR)/gal_webgl_test.wasm
rm -f $(OUTPUT_DIR)/gal_webgl_test.html
# Also clean object files in scenario directories
rm -f ../scenarios/*.o ../scenarios/*.d
.PHONY: all clean
# Include generated dependency files (silently ignore if missing on first build)
-include $(DEPS)

View file

@ -1,870 +0,0 @@
#pragma once
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <memory>
#include <utility>
#include <vector>
namespace mapbox {
namespace util {
template <std::size_t I, typename T>
struct nth {
inline static typename std::tuple_element<I, T>::type get(const T& t) { return std::get<I>(t); };
};
} // namespace util
namespace detail {
template <typename N = uint32_t>
class Earcut {
public:
std::vector<N> indices;
std::size_t vertices = 0;
template <typename Polygon>
void operator()(const Polygon& points);
private:
struct Node {
Node(N index, double x_, double y_) : x(x_), y(y_), i(index), steiner(0) {}
Node(const Node&) = delete;
Node& operator=(const Node&) = delete;
Node(Node&&) = delete;
Node& operator=(Node&&) = delete;
const double x;
const double y;
// previous and next vertice nodes in a polygon ring
Node* prev = nullptr;
Node* next = nullptr;
// z-order curve value
int32_t z = 0;
// original index in polygon
const N i : (sizeof(N) * 8 - 1);
// indicates whether this is a steiner point
N steiner : 1;
// previous and next nodes in z-order
Node* prevZ = nullptr;
Node* nextZ = nullptr;
};
// Cache-optimized Triangle structure for repeated geometric tests
struct Triangle {
const double ax, ay;
const double bx, by;
const double cx, cy;
Triangle(const Node* a, const Node* b, const Node* c)
: ax(a->x), ay(a->y), bx(b->x), by(b->y), cx(c->x), cy(c->y) {}
inline double area() const { return (by - ay) * (cx - bx) - (bx - ax) * (cy - by); }
inline bool containsPoint(double px, double py) const {
return (cx - px) * (ay - py) >= (ax - px) * (cy - py) && (ax - px) * (by - py) >= (bx - px) * (ay - py) &&
(bx - px) * (cy - py) >= (cx - px) * (by - py);
}
};
template <typename Ring>
Node* linkedList(const Ring& points, const bool clockwise);
Node* filterPoints(Node* start, Node* end = nullptr);
void earcutLinked(Node* ear, int pass = 0);
bool isEar(Node* ear);
bool isEarHashed(Node* ear);
Node* cureLocalIntersections(Node* start);
void splitEarcut(Node* start);
template <typename Polygon>
Node* eliminateHoles(const Polygon& points, Node* outerNode);
Node* eliminateHole(Node* hole, Node* outerNode);
Node* findHoleBridge(Node* hole, Node* outerNode);
bool sectorContainsSector(const Node* m, const Node* p);
void indexCurve(Node* start);
Node* sortLinked(Node* list);
int32_t zOrder(const double x_, const double y_);
Node* getLeftmost(Node* start);
bool pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const;
bool isValidDiagonal(Node* a, Node* b);
double area(const Node* p, const Node* q, const Node* r) const;
bool equals(const Node* p1, const Node* p2);
bool intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2);
bool onSegment(const Node* p, const Node* q, const Node* r);
int sign(double val);
bool intersectsPolygon(const Node* a, const Node* b);
bool locallyInside(const Node* a, const Node* b);
bool middleInside(const Node* a, const Node* b);
Node* splitPolygon(Node* a, Node* b);
template <typename Point>
Node* insertNode(std::size_t i, const Point& p, Node* last);
void removeNode(Node* p);
bool hashing;
double minX, maxX;
double minY, maxY;
double inv_size = 0;
template <typename T, typename Alloc = std::allocator<T>>
class ObjectPool {
public:
ObjectPool() { allocateNewBlock(256); }
ObjectPool(std::size_t blockSize_) : baseBlockSize(blockSize_) {
allocateNewBlock(std::max<std::size_t>(blockSize_, 256));
}
~ObjectPool() { clear(); }
template <typename... Args>
T* construct(Args&&... args) {
// If current block is full, move to next block or allocate new one
if (currentIndex >= baseBlockSize) {
currentBlockIndex++;
if (currentBlockIndex < memoryBlocks.size()) {
// Reuse existing block
currentIndex = 0;
} else {
// Allocate a new one
allocateNewBlock(baseBlockSize);
}
}
T* object = memoryBlocks[currentBlockIndex].get() + currentIndex;
alloc_traits::construct(alloc, object, std::forward<Args>(args)...);
totalObjects++;
currentIndex++;
return object;
}
void reset() { clear(); }
void clear() {
// Destroy all objects, but keep blocks allocated for reuse
std::size_t objectsDestroyed = 0;
for (std::size_t blockIdx = 0; blockIdx < memoryBlocks.size() && objectsDestroyed < totalObjects;
++blockIdx) {
// check if we are in the last block
std::size_t objectsInThisBlock = std::min(baseBlockSize, totalObjects - objectsDestroyed);
for (std::size_t i = 0; i < objectsInThisBlock; ++i) {
T* object = memoryBlocks[blockIdx].get() + i;
alloc_traits::destroy(alloc, object);
}
objectsDestroyed += objectsInThisBlock;
}
// Reset to start from first block again
currentBlockIndex = 0;
currentIndex = 0;
totalObjects = 0;
}
private:
Alloc alloc;
typedef typename std::allocator_traits<Alloc> alloc_traits;
// Custom deleter that uses the allocator
struct AllocDeleter {
Alloc alloc;
std::size_t capacity;
void operator()(T* ptr) { alloc_traits::deallocate(alloc, ptr, capacity); }
};
std::vector<std::unique_ptr<T[], AllocDeleter>> memoryBlocks;
std::vector<std::size_t> blockCapacities;
std::size_t currentBlockIndex = 0;
std::size_t currentIndex = 0;
std::size_t totalObjects = 0;
std::size_t baseBlockSize = 256;
void allocateNewBlock(std::size_t capacity) {
T* rawMemory = alloc_traits::allocate(alloc, capacity);
auto newBlock = std::unique_ptr<T[], AllocDeleter>(rawMemory, AllocDeleter{alloc, capacity});
memoryBlocks.push_back(std::move(newBlock));
blockCapacities.push_back(capacity);
currentBlockIndex = memoryBlocks.size() - 1;
currentIndex = 0;
}
};
std::unique_ptr<ObjectPool<Node>> nodes;
std::vector<Node*> holeQueue;
};
template <typename N>
template <typename Polygon>
void Earcut<N>::operator()(const Polygon& points) {
// reset
indices.clear();
vertices = 0;
if (points.empty()) return;
double x;
double y;
int threshold = 80;
std::size_t len = 0;
for (size_t i = 0; threshold >= 0 && i < points.size(); i++) {
threshold -= static_cast<int>(points[i].size());
len += points[i].size();
}
// estimate size of nodes and indices
if (!nodes) {
std::size_t estimatedNodes = len * 3 / 2;
nodes = std::make_unique<ObjectPool<Node>>(std::max<std::size_t>(estimatedNodes, 256));
}
indices.reserve(len + points[0].size());
Node* outerNode = linkedList(points[0], true);
if (!outerNode || outerNode->prev == outerNode->next) return;
if (points.size() > 1) outerNode = eliminateHoles(points, outerNode);
// if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
hashing = threshold < 0;
if (hashing) {
Node* p = outerNode->next;
minX = maxX = outerNode->x;
minY = maxY = outerNode->y;
do {
x = p->x;
y = p->y;
minX = std::min<double>(minX, x);
minY = std::min<double>(minY, y);
maxX = std::max<double>(maxX, x);
maxY = std::max<double>(maxY, y);
p = p->next;
} while (p != outerNode);
// minX, minY and inv_size are later used to transform coords into integers for z-order calculation
inv_size = std::max<double>(maxX - minX, maxY - minY);
inv_size = inv_size != .0 ? (32767. / inv_size) : .0;
}
earcutLinked(outerNode);
nodes->clear();
holeQueue.clear();
}
// create a circular doubly linked list from polygon points in the specified winding order
template <typename N>
template <typename Ring>
typename Earcut<N>::Node* Earcut<N>::linkedList(const Ring& points, const bool clockwise) {
using Point = typename Ring::value_type;
double sum = 0;
const std::size_t len = points.size();
std::size_t i, j;
Node* last = nullptr;
// calculate original winding order of a polygon ring
for (i = 0, j = len > 0 ? len - 1 : 0; i < len; j = i++) {
const auto& p1 = points[i];
const auto& p2 = points[j];
const double p20 = util::nth<0, Point>::get(p2);
const double p10 = util::nth<0, Point>::get(p1);
const double p11 = util::nth<1, Point>::get(p1);
const double p21 = util::nth<1, Point>::get(p2);
sum += (p20 - p10) * (p11 + p21);
}
// link points into circular doubly-linked list in the specified winding order
if (clockwise == (sum > 0)) {
for (i = 0; i < len; i++) last = insertNode(vertices + i, points[i], last);
} else {
for (i = len; i-- > 0;) last = insertNode(vertices + i, points[i], last);
}
if (last && equals(last, last->next)) {
removeNode(last);
last = last->next;
}
vertices += len;
return last;
}
// eliminate colinear or duplicate points
template <typename N>
typename Earcut<N>::Node* Earcut<N>::filterPoints(Node* start, Node* end) {
if (!end) end = start;
Node* p = start;
bool again;
do {
again = false;
if (!p->steiner && (equals(p, p->next) || area(p->prev, p, p->next) == 0)) {
removeNode(p);
p = end = p->prev;
if (p == p->next) break;
again = true;
} else {
p = p->next;
}
} while (again || p != end);
return end;
}
// main ear slicing loop which triangulates a polygon (given as a linked list)
template <typename N>
void Earcut<N>::earcutLinked(Node* ear, int pass) {
if (!ear) return;
// interlink polygon nodes in z-order
if (!pass && hashing) indexCurve(ear);
Node* stop = ear;
Node* prev;
Node* next;
// iterate through ears, slicing them one by one
while (ear->prev != ear->next) {
prev = ear->prev;
next = ear->next;
if (hashing ? isEarHashed(ear) : isEar(ear)) {
// cut off the triangle
indices.emplace_back(prev->i);
indices.emplace_back(ear->i);
indices.emplace_back(next->i);
removeNode(ear);
// skipping the next vertice leads to less sliver triangles
ear = next->next;
stop = next->next;
continue;
}
ear = next;
// if we looped through the whole remaining polygon and can't find any more ears
if (ear == stop) {
// try filtering points and slicing again
if (!pass) earcutLinked(filterPoints(ear), 1);
// if this didn't work, try curing all small self-intersections locally
else if (pass == 1) {
ear = cureLocalIntersections(filterPoints(ear));
earcutLinked(ear, 2);
// as a last resort, try splitting the remaining polygon into two
} else if (pass == 2)
splitEarcut(ear);
break;
}
}
}
// check whether a polygon node forms a valid ear with adjacent nodes
template <typename N>
bool Earcut<N>::isEar(Node* ear) {
const Node* a = ear->prev;
const Node* b = ear;
const Node* c = ear->next;
// Create triangle with cached coordinates and bounding box
const Triangle tri(a, b, c);
if (tri.area() >= 0) return false; // reflex, can't be an ear
// now make sure we don't have other points inside the potential ear
Node* p = ear->next->next;
while (p != ear->prev) {
if (tri.containsPoint(p->x, p->y) && area(p->prev, p, p->next) >= 0) return false;
p = p->next;
}
return true;
}
template <typename N>
bool Earcut<N>::isEarHashed(Node* ear) {
const Node* a = ear->prev;
const Node* b = ear;
const Node* c = ear->next;
// Create triangle with cached coordinates and bounding box
const Triangle tri(a, b, c);
if (tri.area() >= 0) return false; // reflex, can't be an ear
// triangle bbox; min & max are calculated like this for speed
const double minTX = std::min<double>(tri.ax, std::min<double>(tri.bx, tri.cx));
const double minTY = std::min<double>(tri.ay, std::min<double>(tri.by, tri.cy));
const double maxTX = std::max<double>(tri.ax, std::max<double>(tri.bx, tri.cx));
const double maxTY = std::max<double>(tri.ay, std::max<double>(tri.by, tri.cy));
// z-order range for the current triangle bbox;
const int32_t minZ = zOrder(minTX, minTY);
const int32_t maxZ = zOrder(maxTX, maxTY);
// first look for points inside the triangle in increasing z-order
Node* p = ear->nextZ;
while (p && p->z <= maxZ) {
if (p != ear->prev && p != ear->next && tri.containsPoint(p->x, p->y) && area(p->prev, p, p->next) >= 0)
return false;
p = p->nextZ;
}
// then look for points in decreasing z-order
p = ear->prevZ;
while (p && p->z >= minZ) {
if (p != ear->prev && p != ear->next && tri.containsPoint(p->x, p->y) && area(p->prev, p, p->next) >= 0)
return false;
p = p->prevZ;
}
return true;
}
// go through all polygon nodes and cure small local self-intersections
template <typename N>
typename Earcut<N>::Node* Earcut<N>::cureLocalIntersections(Node* start) {
Node* p = start;
do {
Node* a = p->prev;
Node* b = p->next->next;
// a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2])
if (!equals(a, b) && intersects(a, p, p->next, b) && locallyInside(a, b) && locallyInside(b, a)) {
indices.emplace_back(a->i);
indices.emplace_back(p->i);
indices.emplace_back(b->i);
// remove two nodes involved
removeNode(p);
removeNode(p->next);
p = start = b;
}
p = p->next;
} while (p != start);
return filterPoints(p);
}
// try splitting polygon into two and triangulate them independently
template <typename N>
void Earcut<N>::splitEarcut(Node* start) {
// look for a valid diagonal that divides the polygon into two
Node* a = start;
do {
Node* b = a->next->next;
while (b != a->prev) {
if (a->i != b->i && isValidDiagonal(a, b)) {
// split the polygon in two by the diagonal
Node* c = splitPolygon(a, b);
// filter colinear points around the cuts
a = filterPoints(a, a->next);
c = filterPoints(c, c->next);
// run earcut on each half
earcutLinked(a);
earcutLinked(c);
return;
}
b = b->next;
}
a = a->next;
} while (a != start);
}
// link every hole into the outer loop, producing a single-ring polygon without holes
template <typename N>
template <typename Polygon>
typename Earcut<N>::Node* Earcut<N>::eliminateHoles(const Polygon& points, Node* outerNode) {
const size_t len = points.size();
holeQueue.clear();
for (size_t i = 1; i < len; i++) {
Node* list = linkedList(points[i], false);
if (list) {
if (list == list->next) list->steiner = true;
holeQueue.push_back(getLeftmost(list));
}
}
std::sort(holeQueue.begin(), holeQueue.end(), [](const Node* a, const Node* b) { return a->x < b->x; });
// process holes from left to right
for (size_t i = 0; i < holeQueue.size(); i++) {
outerNode = eliminateHole(holeQueue[i], outerNode);
}
return outerNode;
}
// find a bridge between vertices that connects hole with an outer ring and and link it
template <typename N>
typename Earcut<N>::Node* Earcut<N>::eliminateHole(Node* hole, Node* outerNode) {
Node* bridge = findHoleBridge(hole, outerNode);
if (!bridge) {
return outerNode;
}
Node* bridgeReverse = splitPolygon(bridge, hole);
// filter collinear points around the cuts
filterPoints(bridgeReverse, bridgeReverse->next);
// Check if input node was removed by the filtering
return filterPoints(bridge, bridge->next);
}
// David Eberly's algorithm for finding a bridge between hole and outer polygon
template <typename N>
typename Earcut<N>::Node* Earcut<N>::findHoleBridge(Node* hole, Node* outerNode) {
Node* p = outerNode;
double hx = hole->x;
double hy = hole->y;
double qx = -std::numeric_limits<double>::infinity();
Node* m = nullptr;
// find a segment intersected by a ray from the hole's leftmost Vertex to the left;
// segment's endpoint with lesser x will be potential connection Vertex
do {
if (hy <= p->y && hy >= p->next->y && p->next->y != p->y) {
double x = p->x + (hy - p->y) * (p->next->x - p->x) / (p->next->y - p->y);
if (x <= hx && x > qx) {
qx = x;
m = p->x < p->next->x ? p : p->next;
if (x == hx) return m; // hole touches outer segment; pick leftmost endpoint
}
}
p = p->next;
} while (p != outerNode);
if (!m) return 0;
// look for points inside the triangle of hole Vertex, segment intersection and endpoint;
// if there are no points found, we have a valid connection;
// otherwise choose the Vertex of the minimum angle with the ray as connection Vertex
const Node* stop = m;
double tanMin = std::numeric_limits<double>::infinity();
double tanCur = 0;
p = m;
double mx = m->x;
double my = m->y;
do {
if (hx >= p->x && p->x >= mx && hx != p->x &&
pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p->x, p->y)) {
tanCur = std::abs(hy - p->y) / (hx - p->x); // tangential
if (locallyInside(p, hole) &&
(tanCur < tanMin || (tanCur == tanMin && (p->x > m->x || sectorContainsSector(m, p))))) {
m = p;
tanMin = tanCur;
}
}
p = p->next;
} while (p != stop);
return m;
}
// whether sector in vertex m contains sector in vertex p in the same coordinates
template <typename N>
bool Earcut<N>::sectorContainsSector(const Node* m, const Node* p) {
return area(m->prev, m, p->prev) < 0 && area(p->next, m, m->next) < 0;
}
// interlink polygon nodes in z-order
template <typename N>
void Earcut<N>::indexCurve(Node* start) {
assert(start);
Node* p = start;
do {
p->z = p->z ? p->z : zOrder(p->x, p->y);
p->prevZ = p->prev;
p->nextZ = p->next;
p = p->next;
} while (p != start);
p->prevZ->nextZ = nullptr;
p->prevZ = nullptr;
sortLinked(p);
}
// Simon Tatham's linked list merge sort algorithm
// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
template <typename N>
typename Earcut<N>::Node* Earcut<N>::sortLinked(Node* list) {
assert(list);
Node* p;
Node* q;
Node* e;
Node* tail;
int i, numMerges, pSize, qSize;
int inSize = 1;
for (;;) {
p = list;
list = nullptr;
tail = nullptr;
numMerges = 0;
while (p) {
numMerges++;
q = p;
pSize = 0;
for (i = 0; i < inSize; i++) {
pSize++;
q = q->nextZ;
if (!q) break;
}
qSize = inSize;
while (pSize > 0 || (qSize > 0 && q)) {
if (pSize == 0) {
e = q;
q = q->nextZ;
qSize--;
} else if (qSize == 0 || !q) {
e = p;
p = p->nextZ;
pSize--;
} else if (p->z <= q->z) {
e = p;
p = p->nextZ;
pSize--;
} else {
e = q;
q = q->nextZ;
qSize--;
}
if (tail)
tail->nextZ = e;
else
list = e;
e->prevZ = tail;
tail = e;
}
p = q;
}
tail->nextZ = nullptr;
if (numMerges <= 1) return list;
inSize *= 2;
}
}
// z-order of a Vertex given coords and size of the data bounding box
template <typename N>
int32_t Earcut<N>::zOrder(const double x_, const double y_) {
// coords are transformed into non-negative 15-bit integer range
int32_t x = static_cast<int32_t>((x_ - minX) * inv_size);
int32_t y = static_cast<int32_t>((y_ - minY) * inv_size);
x = (x | (x << 8)) & 0x00FF00FF;
x = (x | (x << 4)) & 0x0F0F0F0F;
x = (x | (x << 2)) & 0x33333333;
x = (x | (x << 1)) & 0x55555555;
y = (y | (y << 8)) & 0x00FF00FF;
y = (y | (y << 4)) & 0x0F0F0F0F;
y = (y | (y << 2)) & 0x33333333;
y = (y | (y << 1)) & 0x55555555;
return x | (y << 1);
}
// find the leftmost node of a polygon ring
template <typename N>
typename Earcut<N>::Node* Earcut<N>::getLeftmost(Node* start) {
Node* p = start;
Node* leftmost = start;
do {
if (p->x < leftmost->x || (p->x == leftmost->x && p->y < leftmost->y)) leftmost = p;
p = p->next;
} while (p != start);
return leftmost;
}
// check if a point lies within a convex triangle
template <typename N>
bool Earcut<N>::pointInTriangle(
double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const {
return (cx - px) * (ay - py) >= (ax - px) * (cy - py) && (ax - px) * (by - py) >= (bx - px) * (ay - py) &&
(bx - px) * (cy - py) >= (cx - px) * (by - py);
}
// check if a diagonal between two polygon nodes is valid (lies in polygon interior)
template <typename N>
bool Earcut<N>::isValidDiagonal(Node* a, Node* b) {
return a->next->i != b->i && a->prev->i != b->i && !intersectsPolygon(a, b) && // dones't intersect other edges
((locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
(area(a->prev, a, b->prev) != 0.0 ||
area(a, b->prev, b) != 0.0)) || // does not create opposite-facing sectors
(equals(a, b) && area(a->prev, a, a->next) > 0 &&
area(b->prev, b, b->next) > 0)); // special zero-length case
}
// signed area of a triangle
template <typename N>
double Earcut<N>::area(const Node* p, const Node* q, const Node* r) const {
return (q->y - p->y) * (r->x - q->x) - (q->x - p->x) * (r->y - q->y);
}
// check if two points are equal
template <typename N>
bool Earcut<N>::equals(const Node* p1, const Node* p2) {
return p1->x == p2->x && p1->y == p2->y;
}
// check if two segments intersect
template <typename N>
bool Earcut<N>::intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2) {
int o1 = sign(area(p1, q1, p2));
int o2 = sign(area(p1, q1, q2));
int o3 = sign(area(p2, q2, p1));
int o4 = sign(area(p2, q2, q1));
if (o1 != o2 && o3 != o4) return true; // general case
if (o1 == 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1
if (o2 == 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1
if (o3 == 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2
if (o4 == 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2
return false;
}
// for collinear points p, q, r, check if point q lies on segment pr
template <typename N>
bool Earcut<N>::onSegment(const Node* p, const Node* q, const Node* r) {
return q->x <= std::max<double>(p->x, r->x) && q->x >= std::min<double>(p->x, r->x) &&
q->y <= std::max<double>(p->y, r->y) && q->y >= std::min<double>(p->y, r->y);
}
template <typename N>
int Earcut<N>::sign(double val) {
return (0.0 < val) - (val < 0.0);
}
// check if a polygon diagonal intersects any polygon segments
template <typename N>
bool Earcut<N>::intersectsPolygon(const Node* a, const Node* b) {
const Node* p = a;
do {
if (p->i != a->i && p->next->i != a->i && p->i != b->i && p->next->i != b->i && intersects(p, p->next, a, b))
return true;
p = p->next;
} while (p != a);
return false;
}
// check if a polygon diagonal is locally inside the polygon
template <typename N>
bool Earcut<N>::locallyInside(const Node* a, const Node* b) {
return area(a->prev, a, a->next) < 0 ? area(a, b, a->next) >= 0 && area(a, a->prev, b) >= 0
: area(a, b, a->prev) < 0 || area(a, a->next, b) < 0;
}
// check if the middle Vertex of a polygon diagonal is inside the polygon
template <typename N>
bool Earcut<N>::middleInside(const Node* a, const Node* b) {
const Node* p = a;
bool inside = false;
double px = (a->x + b->x) / 2;
double py = (a->y + b->y) / 2;
do {
if (((p->y > py) != (p->next->y > py)) && p->next->y != p->y &&
(px < (p->next->x - p->x) * (py - p->y) / (p->next->y - p->y) + p->x))
inside = !inside;
p = p->next;
} while (p != a);
return inside;
}
// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits
// polygon into two; if one belongs to the outer ring and another to a hole, it merges it into a
// single ring
template <typename N>
typename Earcut<N>::Node* Earcut<N>::splitPolygon(Node* a, Node* b) {
Node* a2 = nodes->construct(a->i, a->x, a->y);
Node* b2 = nodes->construct(b->i, b->x, b->y);
Node* an = a->next;
Node* bp = b->prev;
a->next = b;
b->prev = a;
a2->next = an;
an->prev = a2;
b2->next = a2;
a2->prev = b2;
bp->next = b2;
b2->prev = bp;
return b2;
}
// create a node and util::optionally link it with previous one (in a circular doubly linked list)
template <typename N>
template <typename Point>
typename Earcut<N>::Node* Earcut<N>::insertNode(std::size_t i, const Point& pt, Node* last) {
Node* p = nodes->construct(static_cast<N>(i), util::nth<0, Point>::get(pt), util::nth<1, Point>::get(pt));
if (!last) {
p->prev = p;
p->next = p;
} else {
assert(last);
p->next = last->next;
p->prev = last;
last->next->prev = p;
last->next = p;
}
return p;
}
template <typename N>
void Earcut<N>::removeNode(Node* p) {
p->next->prev = p->prev;
p->prev->next = p->next;
if (p->prevZ) p->prevZ->nextZ = p->nextZ;
if (p->nextZ) p->nextZ->prevZ = p->prevZ;
}
} // namespace detail
template <typename N = uint32_t, typename Polygon>
std::vector<N> earcut(const Polygon& poly) {
mapbox::detail::Earcut<N> earcut;
earcut(poly);
return std::move(earcut.indices);
}
} // namespace mapbox

View file

@ -1,267 +0,0 @@
/**
* GLU Tesselator implementation using Mapbox earcut
*
* Provides GLU tessellation API for WebGL/WASM builds.
* Uses earcut.hpp for polygon triangulation instead of native GLU library.
*/
#include "earcut.hpp"
#include <vector>
#include <array>
#include <cstring>
// GL types
typedef double GLdouble;
typedef float GLfloat;
typedef unsigned int GLenum;
typedef unsigned char GLboolean;
typedef void GLvoid;
typedef void (*_GLUfuncptr)(void);
// GLU constants
#define GLU_TESS_BEGIN 100100
#define GLU_TESS_VERTEX 100101
#define GLU_TESS_END 100102
#define GLU_TESS_ERROR 100103
#define GLU_TESS_EDGE_FLAG 100104
#define GLU_TESS_COMBINE 100105
#define GLU_TESS_BEGIN_DATA 100106
#define GLU_TESS_VERTEX_DATA 100107
#define GLU_TESS_END_DATA 100108
#define GLU_TESS_ERROR_DATA 100109
#define GLU_TESS_EDGE_FLAG_DATA 100110
#define GLU_TESS_COMBINE_DATA 100111
#define GLU_TESS_WINDING_RULE 100140
#define GLU_TESS_WINDING_ODD 100130
#define GLU_TESS_WINDING_NONZERO 100131
#define GLU_TESS_WINDING_POSITIVE 100132
#define GLU_TESS_WINDING_NEGATIVE 100133
#define GLU_TESS_WINDING_ABS_GEQ_TWO 100134
#ifndef GL_TRUE
#define GL_TRUE 1
#endif
#ifndef GL_FALSE
#define GL_FALSE 0
#endif
// Vertex data stored during tessellation
struct TessVertex
{
std::array<GLdouble, 3> coords;
void* userData; // The data pointer passed to gluTessVertex
};
struct GLUtesselator
{
// Callbacks
void (*vertexCallback)(void* vertex) = nullptr;
void (*vertexDataCallback)(void* vertex, void* userData) = nullptr;
void (*combineCallback)(GLdouble coords[3], void* vertex_data[4],
GLfloat weight[4], void** dataOut) = nullptr;
void (*combineDataCallback)(GLdouble coords[3], void* vertex_data[4],
GLfloat weight[4], void** dataOut, void* userData) = nullptr;
void (*edgeFlagCallback)(GLboolean flag) = nullptr;
void (*edgeFlagDataCallback)(GLboolean flag, void* userData) = nullptr;
void (*errorCallback)(GLenum error) = nullptr;
void (*errorDataCallback)(GLenum error, void* userData) = nullptr;
void (*beginCallback)(GLenum type) = nullptr;
void (*beginDataCallback)(GLenum type, void* userData) = nullptr;
void (*endCallback)() = nullptr;
void (*endDataCallback)(void* userData) = nullptr;
// Contour data - each contour is a list of vertices
std::vector<std::vector<TessVertex>> contours;
std::vector<TessVertex>* currentContour = nullptr;
// User data passed to gluTessBeginPolygon
void* polygonUserData = nullptr;
// Properties
GLenum windingRule = GLU_TESS_WINDING_POSITIVE;
};
extern "C" {
GLUtesselator* gluNewTess()
{
return new GLUtesselator();
}
void gluDeleteTess(GLUtesselator* tess)
{
delete tess;
}
void gluTessCallback(GLUtesselator* tess, GLenum which, _GLUfuncptr fn)
{
if (!tess) return;
switch (which) {
case GLU_TESS_VERTEX:
tess->vertexCallback = (void(*)(void*))fn;
break;
case GLU_TESS_VERTEX_DATA:
tess->vertexDataCallback = (void(*)(void*, void*))fn;
break;
case GLU_TESS_COMBINE:
tess->combineCallback = (void(*)(GLdouble[3], void*[4], GLfloat[4], void**))fn;
break;
case GLU_TESS_COMBINE_DATA:
tess->combineDataCallback = (void(*)(GLdouble[3], void*[4], GLfloat[4], void**, void*))fn;
break;
case GLU_TESS_EDGE_FLAG:
tess->edgeFlagCallback = (void(*)(GLboolean))fn;
break;
case GLU_TESS_EDGE_FLAG_DATA:
tess->edgeFlagDataCallback = (void(*)(GLboolean, void*))fn;
break;
case GLU_TESS_ERROR:
tess->errorCallback = (void(*)(GLenum))fn;
break;
case GLU_TESS_ERROR_DATA:
tess->errorDataCallback = (void(*)(GLenum, void*))fn;
break;
case GLU_TESS_BEGIN:
tess->beginCallback = (void(*)(GLenum))fn;
break;
case GLU_TESS_BEGIN_DATA:
tess->beginDataCallback = (void(*)(GLenum, void*))fn;
break;
case GLU_TESS_END:
tess->endCallback = (void(*)())fn;
break;
case GLU_TESS_END_DATA:
tess->endDataCallback = (void(*)(void*))fn;
break;
}
}
void gluTessProperty(GLUtesselator* tess, GLenum which, GLdouble value)
{
if (!tess) return;
if (which == GLU_TESS_WINDING_RULE)
tess->windingRule = static_cast<GLenum>(value);
}
void gluGetTessProperty(GLUtesselator* tess, GLenum which, GLdouble* value)
{
if (!tess || !value) return;
if (which == GLU_TESS_WINDING_RULE)
*value = static_cast<GLdouble>(tess->windingRule);
}
void gluTessNormal(GLUtesselator* tess, GLdouble x, GLdouble y, GLdouble z)
{
// Ignored - earcut works in 2D (XY plane)
(void)tess; (void)x; (void)y; (void)z;
}
void gluTessBeginPolygon(GLUtesselator* tess, void* userData)
{
if (!tess) return;
tess->contours.clear();
tess->currentContour = nullptr;
tess->polygonUserData = userData;
}
void gluTessBeginContour(GLUtesselator* tess)
{
if (!tess) return;
tess->contours.emplace_back();
tess->currentContour = &tess->contours.back();
}
void gluTessVertex(GLUtesselator* tess, GLdouble coords[3], void* data)
{
if (!tess || !tess->currentContour) return;
TessVertex v;
v.coords = {coords[0], coords[1], coords[2]};
v.userData = data;
tess->currentContour->push_back(v);
}
void gluTessEndContour(GLUtesselator* tess)
{
if (!tess) return;
tess->currentContour = nullptr;
}
void gluTessEndPolygon(GLUtesselator* tess)
{
if (!tess) return;
// Need at least one contour with 3+ vertices
if (tess->contours.empty())
return;
const auto& mainContour = tess->contours[0];
if (mainContour.size() < 3)
return;
// Convert to earcut format: vector of rings, each ring is vector of points
// earcut expects std::array<T, 2> or similar for 2D points
using Point = std::array<double, 2>;
std::vector<std::vector<Point>> polygon;
for (const auto& contour : tess->contours) {
std::vector<Point> ring;
for (const auto& v : contour) {
ring.push_back({v.coords[0], v.coords[1]});
}
polygon.push_back(ring);
}
// Run earcut triangulation
std::vector<uint32_t> indices = mapbox::earcut<uint32_t>(polygon);
// Build flat vertex list for index lookup
std::vector<const TessVertex*> allVertices;
for (const auto& contour : tess->contours) {
for (const auto& v : contour) {
allVertices.push_back(&v);
}
}
// Call edge flag callback to indicate we're producing triangles
// (edge flag callback forces GLU to output only triangles, which earcut always does)
if (tess->edgeFlagDataCallback)
tess->edgeFlagDataCallback(GL_TRUE, tess->polygonUserData);
else if (tess->edgeFlagCallback)
tess->edgeFlagCallback(GL_TRUE);
// Output triangles via vertex callback
// Each triangle is 3 consecutive indices
for (size_t i = 0; i < indices.size(); i += 3) {
// Get vertex indices for this triangle
uint32_t idx0 = indices[i];
uint32_t idx1 = indices[i + 1];
uint32_t idx2 = indices[i + 2];
// Emit the three vertices
if (tess->vertexDataCallback) {
tess->vertexDataCallback(allVertices[idx0]->userData, tess->polygonUserData);
tess->vertexDataCallback(allVertices[idx1]->userData, tess->polygonUserData);
tess->vertexDataCallback(allVertices[idx2]->userData, tess->polygonUserData);
} else if (tess->vertexCallback) {
tess->vertexCallback(allVertices[idx0]->userData);
tess->vertexCallback(allVertices[idx1]->userData);
tess->vertexCallback(allVertices[idx2]->userData);
}
}
}
const unsigned char* gluErrorString(GLenum error)
{
static const unsigned char errStr[] = "GLU tesselator error";
(void)error;
return errStr;
}
} // extern "C"

File diff suppressed because it is too large Load diff

View file

@ -1,132 +0,0 @@
/**
* Copyright (C) 2013 Jorge Jimenez (jorge@iryoku.com)
* Copyright (C) 2013 Jose I. Echevarria (joseignacioechevarria@gmail.com)
* Copyright (C) 2013 Belen Masia (bmasia@unizar.es)
* Copyright (C) 2013 Fernando Navarro (fernandn@microsoft.com)
* Copyright (C) 2013 Diego Gutierrez (diegog@unizar.es)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to
* do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software. As clarification, there
* is no requirement that the copyright notice and permission be included in
* binary distributions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef SEARCHTEX_H
#define SEARCHTEX_H
#define SEARCHTEX_WIDTH 64
#define SEARCHTEX_HEIGHT 16
#define SEARCHTEX_PITCH SEARCHTEX_WIDTH
#define SEARCHTEX_SIZE (SEARCHTEX_HEIGHT * SEARCHTEX_PITCH)
/**
* Stored in R8 format. Load it in the following format:
* - DX9: D3DFMT_L8
* - DX10: DXGI_FORMAT_R8_UNORM
*/
static const unsigned char searchTexBytes[] = {
0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00,
0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0xfe, 0x7f, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe,
0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f,
0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00,
0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0xfe, 0x7f, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe,
0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f,
0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f,
0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f,
0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f,
0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00,
0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f,
0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f,
0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f,
0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00,
0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
};
#endif

View file

@ -1,143 +0,0 @@
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef OPENGL_ANTIALIASING_H__
#define OPENGL_ANTIALIASING_H__
#include <memory>
#include "shader.h"
#include <math/vector2d.h>
namespace KIGFX {
class WEBGL_COMPOSITOR;
class OPENGL_PRESENTOR
{
public:
virtual ~OPENGL_PRESENTOR()
{
}
virtual bool Init() = 0;
virtual unsigned int CreateBuffer() = 0;
virtual VECTOR2I GetInternalBufferSize() = 0;
virtual void OnLostBuffers() = 0;
virtual void Begin() = 0;
virtual void DrawBuffer( GLuint aBuffer ) = 0;
virtual void Present() = 0;
};
class ANTIALIASING_NONE : public OPENGL_PRESENTOR
{
public:
ANTIALIASING_NONE( WEBGL_COMPOSITOR* aCompositor );
bool Init() override;
unsigned int CreateBuffer() override;
VECTOR2I GetInternalBufferSize() override;
void OnLostBuffers() override;
void Begin() override;
void DrawBuffer( GLuint aBuffer ) override;
void Present() override;
private:
WEBGL_COMPOSITOR* compositor;
};
class ANTIALIASING_SUPERSAMPLING : public OPENGL_PRESENTOR
{
public:
ANTIALIASING_SUPERSAMPLING( WEBGL_COMPOSITOR* aCompositor );
bool Init() override;
unsigned int CreateBuffer() override;
VECTOR2I GetInternalBufferSize() override;
void OnLostBuffers() override;
void Begin() override;
void DrawBuffer( GLuint ) override;
void Present() override;
private:
WEBGL_COMPOSITOR* compositor;
unsigned int ssaaMainBuffer;
bool areBuffersCreated;
bool areShadersCreated;
};
class ANTIALIASING_SMAA : public OPENGL_PRESENTOR
{
public:
ANTIALIASING_SMAA( WEBGL_COMPOSITOR* aCompositor );
bool Init() override;
unsigned int CreateBuffer () override;
VECTOR2I GetInternalBufferSize() override;
void OnLostBuffers() override;
void Begin() override;
void DrawBuffer( GLuint buffer ) override;
void Present() override;
private:
void loadShaders();
void updateUniforms();
bool areBuffersInitialized;
unsigned int smaaBaseBuffer; // base + overlay temporary
unsigned int smaaEdgesBuffer;
unsigned int smaaBlendBuffer;
// smaa shader lookup textures
unsigned int smaaAreaTex;
unsigned int smaaSearchTex;
bool shadersLoaded;
std::unique_ptr<SHADER> pass_1_shader;
GLint pass_1_metrics;
std::unique_ptr<SHADER> pass_2_shader;
GLint pass_2_metrics;
std::unique_ptr<SHADER> pass_3_shader;
GLint pass_3_metrics;
WEBGL_COMPOSITOR* compositor;
};
}
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -1,442 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright 2013-2017 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file cached_container.cpp
* @brief Class to store instances of VERTEX with caching. It allows storing VERTEX objects and
* associates them with VERTEX_ITEMs. This leads to a possibility of caching vertices data in the
* GPU memory and a fast reuse of that data.
*/
#include "cached_container.h"
#include "vertex_manager.h"
#include "vertex_item.h"
#include "utils.h"
#include <list>
#include <algorithm>
#include <cassert>
#ifdef __WIN32__
#include <excpt.h>
#endif
#ifdef KICAD_GAL_PROFILE
#include <wx/log.h>
#include <core/profile.h>
#endif /* KICAD_GAL_PROFILE */
using namespace KIGFX;
CACHED_CONTAINER::CACHED_CONTAINER( unsigned int aSize ) :
VERTEX_CONTAINER( aSize ),
m_item( nullptr ),
m_chunkSize( 0 ),
m_chunkOffset( 0 ),
m_maxIndex( 0 )
{
// In the beginning there is only free space
m_freeChunks.insert( std::make_pair( aSize, 0 ) );
}
void CACHED_CONTAINER::SetItem( VERTEX_ITEM* aItem )
{
assert( aItem != nullptr );
unsigned int itemSize = aItem->GetSize();
m_item = aItem;
m_chunkSize = itemSize;
// Get the previously set offset if the item was stored previously
m_chunkOffset = itemSize > 0 ? aItem->GetOffset() : -1;
}
void CACHED_CONTAINER::FinishItem()
{
assert( m_item != nullptr );
unsigned int itemSize = m_item->GetSize();
// Finishing the previously edited item
if( itemSize < m_chunkSize )
{
// There is some not used but reserved memory left, so we should return it to the pool
int itemOffset = m_item->GetOffset();
// Add the not used memory back to the pool
addFreeChunk( itemOffset + itemSize, m_chunkSize - itemSize );
// mergeFreeChunks(); // veery slow and buggy
m_maxIndex = std::max( itemOffset + itemSize, m_maxIndex );
}
if( itemSize > 0 )
m_items.insert( m_item );
m_item = nullptr;
m_chunkSize = 0;
m_chunkOffset = 0;
#if CACHED_CONTAINER_TEST > 1
test();
#endif
}
VERTEX* CACHED_CONTAINER::Allocate( unsigned int aSize )
{
assert( m_item != nullptr );
assert( IsMapped() );
if( m_failed )
return nullptr;
unsigned int itemSize = m_item->GetSize();
unsigned int newSize = itemSize + aSize;
if( newSize > m_chunkSize )
{
// There is not enough space in the currently reserved chunk, so we have to resize it
if( !reallocate( newSize ) )
{
m_failed = true;
return nullptr;
}
}
VERTEX* reserved = &m_vertices[m_chunkOffset + itemSize];
// Now the item officially possesses the memory chunk
m_item->setSize( newSize );
// The content has to be updated
m_dirty = true;
#if CACHED_CONTAINER_TEST > 0
test();
#endif
#if CACHED_CONTAINER_TEST > 2
showFreeChunks();
showUsedChunks();
#endif
return reserved;
}
void CACHED_CONTAINER::Delete( VERTEX_ITEM* aItem )
{
assert( aItem != nullptr );
assert( m_items.find( aItem ) != m_items.end() || aItem->GetSize() == 0 );
int size = aItem->GetSize();
if( size == 0 )
return; // Item is not stored here
int offset = aItem->GetOffset();
// Insert a free memory chunk entry in the place where item was stored
addFreeChunk( offset, size );
// Indicate that the item is not stored in the container anymore
aItem->setSize( 0 );
m_items.erase( aItem );
#if CACHED_CONTAINER_TEST > 0
test();
#endif
// This dynamic memory freeing optimize memory usage, but in fact can create
// out of memory issues because freeing and reallocation large chunks of memory
// can create memory fragmentation and no room to reallocate large chunks
// after many free/reallocate cycles during a session using the same complex board
// So it can be disable.
// Currently: it is disable to avoid "out of memory" issues
#if 0
// Dynamic memory freeing, there is no point in holding
// a large amount of memory when there is no use for it
if( m_freeSpace > ( 0.75 * m_currentSize ) && m_currentSize > m_initialSize )
{
defragmentResize( 0.5 * m_currentSize );
}
#endif
}
void CACHED_CONTAINER::Clear()
{
m_freeSpace = m_currentSize;
m_maxIndex = 0;
m_failed = false;
// Set the size of all the stored VERTEX_ITEMs to 0, so it is clear that they are not held
// in the container anymore
for( ITEMS::iterator it = m_items.begin(); it != m_items.end(); ++it )
( *it )->setSize( 0 );
m_items.clear();
// Now there is only free space left
m_freeChunks.clear();
m_freeChunks.insert( std::make_pair( m_freeSpace, 0 ) );
}
bool CACHED_CONTAINER::reallocate( unsigned int aSize )
{
assert( aSize > 0 );
assert( IsMapped() );
unsigned int itemSize = m_item->GetSize();
// Find a free space chunk >= aSize
FREE_CHUNK_MAP::iterator newChunk = m_freeChunks.lower_bound( aSize );
// Is there enough space to store vertices?
if( newChunk == m_freeChunks.end() )
{
bool result;
// Would it be enough to double the current space?
if( aSize < m_freeSpace + m_currentSize )
{
// Yes: exponential growing
result = defragmentResize( m_currentSize * 2 );
}
else
{
// No: grow to the nearest greater power of 2
result = defragmentResize( pow( 2, ceil( log2( m_currentSize * 2 + aSize ) ) ) );
}
if( !result )
return false;
newChunk = m_freeChunks.lower_bound( aSize );
assert( newChunk != m_freeChunks.end() );
}
// Parameters of the allocated chunk
unsigned int newChunkSize = getChunkSize( *newChunk );
unsigned int newChunkOffset = getChunkOffset( *newChunk );
assert( newChunkSize >= aSize );
assert( newChunkOffset < m_currentSize );
// Check if the item was previously stored in the container
if( itemSize > 0 )
{
// The item was reallocated, so we have to copy all the old data to the new place
memcpy( &m_vertices[newChunkOffset], &m_vertices[m_chunkOffset], itemSize * VERTEX_SIZE );
// Free the space used by the previous chunk
addFreeChunk( m_chunkOffset, m_chunkSize );
}
// Remove the new allocated chunk from the free space pool
m_freeChunks.erase( newChunk );
m_freeSpace -= newChunkSize;
m_chunkSize = newChunkSize;
m_chunkOffset = newChunkOffset;
m_item->setOffset( m_chunkOffset );
return true;
}
void CACHED_CONTAINER::defragment( VERTEX* aTarget )
{
// Defragmentation
ITEMS::iterator it, it_end;
int newOffset = 0;
[&]()
{
#ifdef __WIN32__
#ifdef __MINGW32__
// currently, because SEH (Structured Exception Handling) is not documented on msys
// (for instance __try or __try1 exists without doc) or is not supported, do nothing
#else
__try
#endif
#endif
{
for( VERTEX_ITEM* item : m_items )
{
int itemOffset = item->GetOffset();
int itemSize = item->GetSize();
// Move an item to the new container
memcpy( &aTarget[newOffset], &m_vertices[itemOffset], itemSize * VERTEX_SIZE );
// Update new offset
item->setOffset( newOffset );
// Move to the next free space
newOffset += itemSize;
}
// Move the current item and place it at the end
if( m_item->GetSize() > 0 )
{
memcpy( &aTarget[newOffset], &m_vertices[m_item->GetOffset()],
m_item->GetSize() * VERTEX_SIZE );
m_item->setOffset( newOffset );
m_chunkOffset = newOffset;
}
}
#ifdef __WIN32__
#ifdef __MINGW32__
// currently, because SEH (Structured Exception Handling) is not documented on msys
// (for instance __except1 exists without doc) or is not supported, do nothing
#else
__except( GetExceptionCode() == STATUS_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER
: EXCEPTION_CONTINUE_SEARCH )
{
throw std::runtime_error(
"Access violation in defragment. This is usually an indicator of "
"system or GPU memory running low." );
};
#endif
#endif
}();
m_maxIndex = usedSpace();
}
void CACHED_CONTAINER::mergeFreeChunks()
{
if( m_freeChunks.size() <= 1 ) // There are no chunks that can be merged
return;
#ifdef KICAD_GAL_PROFILE
PROF_TIMER totalTime;
#endif /* KICAD_GAL_PROFILE */
// Reversed free chunks map - this one stores chunk size with its offset as the key
std::list<CHUNK> freeChunks;
FREE_CHUNK_MAP::const_iterator it, it_end;
for( it = m_freeChunks.begin(), it_end = m_freeChunks.end(); it != it_end; ++it )
{
freeChunks.emplace_back( it->second, it->first );
}
m_freeChunks.clear();
freeChunks.sort();
std::list<CHUNK>::const_iterator itf, itf_end;
unsigned int offset = freeChunks.front().first;
unsigned int size = freeChunks.front().second;
freeChunks.pop_front();
for( itf = freeChunks.begin(), itf_end = freeChunks.end(); itf != itf_end; ++itf )
{
if( itf->first == offset + size )
{
// These chunks can be merged, so just increase the current chunk size and go on
size += itf->second;
}
else
{
// These chunks cannot be merged
// So store the previous one
m_freeChunks.insert( std::make_pair( size, offset ) );
// and let's check the next chunk
offset = itf->first;
size = itf->second;
}
}
// Add the last one
m_freeChunks.insert( std::make_pair( size, offset ) );
#if CACHED_CONTAINER_TEST > 0
test();
#endif
}
void CACHED_CONTAINER::addFreeChunk( unsigned int aOffset, unsigned int aSize )
{
assert( aOffset + aSize <= m_currentSize );
assert( aSize > 0 );
m_freeChunks.insert( std::make_pair( aSize, aOffset ) );
m_freeSpace += aSize;
}
void CACHED_CONTAINER::showFreeChunks()
{
}
void CACHED_CONTAINER::showUsedChunks()
{
}
void CACHED_CONTAINER::test()
{
#ifdef KICAD_GAL_PROFILE
// Free space check
unsigned int freeSpace = 0;
FREE_CHUNK_MAP::iterator itf;
for( itf = m_freeChunks.begin(); itf != m_freeChunks.end(); ++itf )
freeSpace += getChunkSize( *itf );
assert( freeSpace == m_freeSpace );
// Used space check
unsigned int used_space = 0;
ITEMS::iterator itr;
for( itr = m_items.begin(); itr != m_items.end(); ++itr )
used_space += ( *itr )->GetSize();
// If we have a chunk assigned, then there must be an item edited
assert( m_chunkSize == 0 || m_item );
// Currently reserved chunk is also counted as used
used_space += m_chunkSize;
assert( ( m_freeSpace + used_space ) == m_currentSize );
// Overlapping check TODO
#endif /* KICAD_GAL_PROFILE */
}

View file

@ -1,191 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright 2013-2017 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef CACHED_CONTAINER_H_
#define CACHED_CONTAINER_H_
#include "vertex_container.h"
#include <map>
#include <set>
namespace KIGFX
{
class VERTEX_ITEM;
class SHADER;
/**
* Class to store VERTEX instances with caching.
*
* It associates VERTEX objects and with VERTEX_ITEMs. Caching vertices data in the memory and a
* enables fast reuse of that data.
*/
class CACHED_CONTAINER : public VERTEX_CONTAINER
{
public:
CACHED_CONTAINER( unsigned int aSize = DEFAULT_SIZE );
virtual ~CACHED_CONTAINER() {}
bool IsCached() const override
{
return true;
}
virtual void SetItem( VERTEX_ITEM* aItem ) override;
///< @copydoc VERTEX_CONTAINER::FinishItem()
virtual void FinishItem() override;
/**
* Return allocated space for the requested number of vertices associated with the
* current item (set with SetItem()).
*
* The allocated space is added at the end of the chunk used by the current item and
* may serve to store new vertices.
*
* @param aSize is the number of vertices to be allocated.
* @return Pointer to the allocated space.
* @throw bad_alloc exception if allocation fails.
*/
virtual VERTEX* Allocate( unsigned int aSize ) override;
///< @copydoc VERTEX_CONTAINER::Delete()
virtual void Delete( VERTEX_ITEM* aItem ) override;
///< @copydoc VERTEX_CONTAINER::Clear()
virtual void Clear() override;
/**
* Return handle to the vertex buffer. It might be negative if the buffer is not initialized.
*/
virtual unsigned int GetBufferHandle() const = 0;
/**
* Return true if vertex buffer is currently mapped.
*/
virtual bool IsMapped() const = 0;
///< @copydoc VERTEX_CONTAINER::Map()
virtual void Map() override = 0;
///< @copydoc VERTEX_CONTAINER::Unmap()
virtual void Unmap() override = 0;
virtual unsigned int AllItemsSize() const { return 0; }
protected:
///< Maps size of free memory chunks to their offsets
typedef std::pair<unsigned int, unsigned int> CHUNK;
typedef std::multimap<unsigned int, unsigned int> FREE_CHUNK_MAP;
/// List of all the stored items
typedef std::set<VERTEX_ITEM*> ITEMS;
/**
* Resize the chunk that stores the current item to the given size. The current item has
* its offset adjusted after the call, and the new chunk parameters are stored
* in m_chunkOffset and m_chunkSize.
*
* @param aSize is the requested chunk size.
* @return true in case of success, false otherwise.
*/
bool reallocate( unsigned int aSize );
/**
* Remove empty spaces between chunks and optionally resizes the container.
*
* After the operation there is continuous space for storing vertices at the end of the
* container.
*
* @param aNewSize is the new size of container, expressed in number of vertices.
* @return false in case of failure (e.g. memory shortage).
*/
virtual bool defragmentResize( unsigned int aNewSize ) = 0;
/**
* Transfer all stored data to a new buffer, removing empty spaces between the data chunks
* in the container.
*
* @param aTarget is the destination for the defragmented data.
*/
void defragment( VERTEX* aTarget );
/**
* Look for consecutive free memory chunks and merges them, decreasing fragmentation of
* memory.
*/
void mergeFreeChunks();
/**
* Return the size of a chunk.
*
* @param aChunk is the chunk.
*/
inline int getChunkSize( const CHUNK& aChunk ) const
{
return aChunk.first;
}
/**
* Return the offset of a chunk.
*
* @param aChunk is the chunk.
*/
inline unsigned int getChunkOffset( const CHUNK& aChunk ) const
{
return aChunk.second;
}
/**
* Add a chunk marked as a free space.
*/
void addFreeChunk( unsigned int aOffset, unsigned int aSize );
///< Store size & offset of free chunks.
FREE_CHUNK_MAP m_freeChunks;
///< Stored VERTEX_ITEMs
ITEMS m_items;
///< Currently modified item
VERTEX_ITEM* m_item;
///< Properties of currently modified chunk & item
unsigned int m_chunkSize;
unsigned int m_chunkOffset;
///< Maximal vertex index number stored in the container
unsigned int m_maxIndex;
private:
/// Debug & test functions
void showFreeChunks();
void showUsedChunks();
void test();
};
} // namespace KIGFX
#endif /* CACHED_CONTAINER_H_ */

View file

@ -1,312 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright 2013-2017 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "cached_container_gpu.h"
#include "vertex_manager.h"
#include "vertex_item.h"
#include "shader.h"
#include "utils.h"
#include <wx/log.h>
#include <list>
#include <core/profile.h>
#include <trace_helpers.h>
using namespace KIGFX;
/**
* Flag to enable debug output of the GAL OpenGL GPU cached container.
*
* Use "KICAD_GAL_CACHED_CONTAINER_GPU" to enable GAL OpenGL GPU cached container tracing.
*
* @ingroup trace_env_vars
*/
static const wxChar* const traceGalCachedContainerGpu = wxT( "KICAD_GAL_CACHED_CONTAINER_GPU" );
CACHED_CONTAINER_GPU::CACHED_CONTAINER_GPU( unsigned int aSize ) :
CACHED_CONTAINER( aSize ),
m_isMapped( false ),
m_glBufferHandle( -1 )
{
m_useCopyBuffer = GLEW_ARB_copy_buffer;
wxString vendor( glGetString( GL_VENDOR ) );
// workaround for intel GPU drivers:
// disable glCopyBuffer, causes crashes/freezes on certain driver versions
// Note, Intel's GL_VENDOR string varies depending on GPU/driver generation
// But generally always starts with Intel at least
if( vendor.StartsWith( "Intel" ) || vendor.Contains( "etnaviv" ) )
{
m_useCopyBuffer = false;
}
KI_TRACE( traceGalProfile, "VBO initial size: %d\n", m_currentSize );
glGenBuffers( 1, &m_glBufferHandle );
glBindBuffer( GL_ARRAY_BUFFER, m_glBufferHandle );
glBufferData( GL_ARRAY_BUFFER, m_currentSize * VERTEX_SIZE, nullptr, GL_DYNAMIC_DRAW );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
checkGlError( "allocating video memory for cached container", __FILE__, __LINE__ );
}
CACHED_CONTAINER_GPU::~CACHED_CONTAINER_GPU()
{
if( m_isMapped )
Unmap();
if( glDeleteBuffers )
glDeleteBuffers( 1, &m_glBufferHandle );
}
void CACHED_CONTAINER_GPU::Map()
{
wxCHECK( !IsMapped(), /*void*/ );
// OpenGL version might suddenly stop being available in Windows when an RDP session is started
if( !glBindBuffer )
throw std::runtime_error( "OpenGL no longer available!" );
glBindBuffer( GL_ARRAY_BUFFER, m_glBufferHandle );
m_vertices = static_cast<VERTEX*>( glMapBuffer( GL_ARRAY_BUFFER, GL_READ_WRITE ) );
if( checkGlError( "mapping vertices buffer", __FILE__, __LINE__ ) == GL_NO_ERROR )
m_isMapped = true;
}
void CACHED_CONTAINER_GPU::Unmap()
{
wxCHECK( IsMapped(), /*void*/ );
// This gets called from ~CACHED_CONTAINER_GPU. To avoid throwing an exception from
// the dtor, catch it here instead.
try
{
glUnmapBuffer( GL_ARRAY_BUFFER );
checkGlError( "unmapping vertices buffer", __FILE__, __LINE__ );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
m_vertices = nullptr;
checkGlError( "unbinding vertices buffer", __FILE__, __LINE__ );
}
catch( const std::runtime_error& err )
{
wxLogError( wxT( "OpenGL did not shut down properly.\n\n%s" ), err.what() );
}
m_isMapped = false;
}
bool CACHED_CONTAINER_GPU::defragmentResize( unsigned int aNewSize )
{
if( !m_useCopyBuffer )
return defragmentResizeMemcpy( aNewSize );
wxCHECK( IsMapped(), false );
wxLogTrace( traceGalCachedContainerGpu,
wxT( "Resizing & defragmenting container from %d to %d" ), m_currentSize,
aNewSize );
// No shrinking if we cannot fit all the data
if( usedSpace() > aNewSize )
return false;
#ifdef KICAD_GAL_PROFILE
PROF_TIMER totalTime;
#endif /* KICAD_GAL_PROFILE */
GLuint newBuffer;
// glCopyBufferSubData requires a buffer to be unmapped
glUnmapBuffer( GL_ARRAY_BUFFER );
// Create a new destination buffer
glGenBuffers( 1, &newBuffer );
// It would be best to use GL_COPY_WRITE_BUFFER here,
// but it is not available everywhere
#ifdef KICAD_GAL_PROFILE
GLint eaBuffer = -1;
glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, &eaBuffer );
wxASSERT( eaBuffer == 0 );
#endif /* KICAD_GAL_PROFILE */
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, newBuffer );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, aNewSize * VERTEX_SIZE, nullptr, GL_DYNAMIC_DRAW );
checkGlError( "creating buffer during defragmentation", __FILE__, __LINE__ );
ITEMS::iterator it, it_end;
int newOffset = 0;
// Defragmentation
for( it = m_items.begin(), it_end = m_items.end(); it != it_end; ++it )
{
VERTEX_ITEM* item = *it;
int itemOffset = item->GetOffset();
int itemSize = item->GetSize();
// Move an item to the new container
glCopyBufferSubData( GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, itemOffset * VERTEX_SIZE,
newOffset * VERTEX_SIZE, itemSize * VERTEX_SIZE );
// Update new offset
item->setOffset( newOffset );
// Move to the next free space
newOffset += itemSize;
}
// Move the current item and place it at the end
if( m_item->GetSize() > 0 )
{
glCopyBufferSubData( GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER,
m_item->GetOffset() * VERTEX_SIZE, newOffset * VERTEX_SIZE,
m_item->GetSize() * VERTEX_SIZE );
m_item->setOffset( newOffset );
m_chunkOffset = newOffset;
}
// Cleanup
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
// Previously we have unmapped the array buffer, now when it is also
// unbound, it may be officially marked as unmapped
m_isMapped = false;
glDeleteBuffers( 1, &m_glBufferHandle );
// Switch to the new vertex buffer
m_glBufferHandle = newBuffer;
Map();
checkGlError( "switching buffers during defragmentation", __FILE__, __LINE__ );
#ifdef KICAD_GAL_PROFILE
totalTime.Stop();
wxLogTrace( traceGalCachedContainerGpu, "Defragmented container storing %d vertices / %.1f ms",
m_currentSize - m_freeSpace, totalTime.msecs() );
#endif /* KICAD_GAL_PROFILE */
m_freeSpace += ( aNewSize - m_currentSize );
m_currentSize = aNewSize;
KI_TRACE( traceGalProfile, "VBO size %d used %d\n", m_currentSize, AllItemsSize() );
// Now there is only one big chunk of free memory
m_freeChunks.clear();
m_freeChunks.insert( std::make_pair( m_freeSpace, m_currentSize - m_freeSpace ) );
return true;
}
bool CACHED_CONTAINER_GPU::defragmentResizeMemcpy( unsigned int aNewSize )
{
wxCHECK( IsMapped(), false );
wxLogTrace( traceGalCachedContainerGpu,
wxT( "Resizing & defragmenting container (memcpy) from %d to %d" ), m_currentSize,
aNewSize );
// No shrinking if we cannot fit all the data
if( usedSpace() > aNewSize )
return false;
#ifdef KICAD_GAL_PROFILE
PROF_TIMER totalTime;
#endif /* KICAD_GAL_PROFILE */
GLuint newBuffer;
VERTEX* newBufferMem;
// Create the destination buffer
glGenBuffers( 1, &newBuffer );
// It would be best to use GL_COPY_WRITE_BUFFER here,
// but it is not available everywhere
#ifdef KICAD_GAL_PROFILE
GLint eaBuffer = -1;
glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, &eaBuffer );
wxASSERT( eaBuffer == 0 );
#endif /* KICAD_GAL_PROFILE */
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, newBuffer );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, aNewSize * VERTEX_SIZE, nullptr, GL_DYNAMIC_DRAW );
newBufferMem = static_cast<VERTEX*>( glMapBuffer( GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY ) );
checkGlError( "creating buffer during defragmentation", __FILE__, __LINE__ );
defragment( newBufferMem );
// Cleanup
glUnmapBuffer( GL_ELEMENT_ARRAY_BUFFER );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
Unmap();
glDeleteBuffers( 1, &m_glBufferHandle );
// Switch to the new vertex buffer
m_glBufferHandle = newBuffer;
Map();
checkGlError( "switching buffers during defragmentation", __FILE__, __LINE__ );
#ifdef KICAD_GAL_PROFILE
totalTime.Stop();
wxLogTrace( traceGalCachedContainerGpu, "Defragmented container storing %d vertices / %.1f ms",
m_currentSize - m_freeSpace, totalTime.msecs() );
#endif /* KICAD_GAL_PROFILE */
m_freeSpace += ( aNewSize - m_currentSize );
m_currentSize = aNewSize;
KI_TRACE( traceGalProfile, "VBO size %d used: %d \n", m_currentSize, AllItemsSize() );
// Now there is only one big chunk of free memory
m_freeChunks.clear();
m_freeChunks.insert( std::make_pair( m_freeSpace, m_currentSize - m_freeSpace ) );
return true;
}
unsigned int CACHED_CONTAINER_GPU::AllItemsSize() const
{
unsigned int size = 0;
for( const auto& item : m_items )
{
size += item->GetSize();
}
return size;
}

View file

@ -1,87 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright 2013-2017 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef CACHED_CONTAINER_GPU_H_
#define CACHED_CONTAINER_GPU_H_
#include "cached_container.h"
namespace KIGFX
{
/**
* Specialization of CACHED_CONTAINER that stores data in video memory via memory mapping.
*/
class CACHED_CONTAINER_GPU : public CACHED_CONTAINER
{
public:
CACHED_CONTAINER_GPU( unsigned int aSize = DEFAULT_SIZE );
~CACHED_CONTAINER_GPU();
unsigned int GetBufferHandle() const override
{
return m_glBufferHandle;
}
bool IsMapped() const override
{
return m_isMapped;
}
///< @copydoc VERTEX_CONTAINER::Map()
void Map() override;
///< @copydoc VERTEX_CONTAINER::Unmap()
void Unmap() override;
virtual unsigned int AllItemsSize() const override;
protected:
/**
* Remove empty spaces between chunks and optionally resizes the container.
*
* After the operation there is continuous space for storing vertices at the end of
* the container.
*
* @param aNewSize is the new size of container, expressed in number of vertices.
* @return false in case of failure (e.g. memory shortage).
*/
bool defragmentResize( unsigned int aNewSize ) override;
bool defragmentResizeMemcpy( unsigned int aNewSize );
///< Flag saying if vertex buffer is currently mapped
bool m_isMapped;
///< Vertex buffer handle
unsigned int m_glBufferHandle;
///< Flag saying whether it is safe to use glCopyBufferSubData
bool m_useCopyBuffer;
};
} // namespace KIGFX
#endif /* CACHED_CONTAINER_GPU_H_ */

View file

@ -1,134 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright 2013-2017 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "cached_container_ram.h"
#include "vertex_manager.h"
#include "vertex_item.h"
#include "shader.h"
#include "utils.h"
#include <confirm.h>
#include <list>
#include <cassert>
#include <wx/log.h>
#ifdef KICAD_GAL_PROFILE
#include <core/profile.h>
#endif /* KICAD_GAL_PROFILE */
using namespace KIGFX;
/**
* Flag to enable debug output of the GAL OpenGL cached container.
*
* Use "KICAD_GAL_CACHED_CONTAINER" to enable GAL OpenGL cached container tracing.
*
* @ingroup trace_env_vars
*/
static const wxChar* const traceGalCachedContainer = wxT( "KICAD_GAL_CACHED_CONTAINER" );
CACHED_CONTAINER_RAM::CACHED_CONTAINER_RAM( unsigned int aSize ) :
CACHED_CONTAINER( aSize ),
m_verticesBuffer( 0 )
{
glGenBuffers( 1, &m_verticesBuffer );
checkGlError( "generating vertices buffer", __FILE__, __LINE__ );
m_vertices = static_cast<VERTEX*>( malloc( aSize * VERTEX_SIZE ) );
if( !m_vertices )
throw std::bad_alloc();
}
CACHED_CONTAINER_RAM::~CACHED_CONTAINER_RAM()
{
if( glDeleteBuffers )
glDeleteBuffers( 1, &m_verticesBuffer );
free( m_vertices );
}
void CACHED_CONTAINER_RAM::Unmap()
{
if( !m_dirty )
return;
// Upload vertices coordinates and shader types to GPU memory
glBindBuffer( GL_ARRAY_BUFFER, m_verticesBuffer );
checkGlError( "binding vertices buffer", __FILE__, __LINE__ );
glBufferData( GL_ARRAY_BUFFER, m_maxIndex * VERTEX_SIZE, m_vertices, GL_STREAM_DRAW );
checkGlError( "transferring vertices", __FILE__, __LINE__ );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
checkGlError( "unbinding vertices buffer", __FILE__, __LINE__ );
}
bool CACHED_CONTAINER_RAM::defragmentResize( unsigned int aNewSize )
{
wxLogTrace( traceGalCachedContainer,
wxT( "Resizing & defragmenting container (memcpy) from %d to %d" ), m_currentSize,
aNewSize );
// No shrinking if we cannot fit all the data
if( usedSpace() > aNewSize )
return false;
#ifdef KICAD_GAL_PROFILE
PROF_TIMER totalTime;
#endif /* KICAD_GAL_PROFILE */
VERTEX* newBufferMem = static_cast<VERTEX*>( malloc( aNewSize * VERTEX_SIZE ) );
if( !newBufferMem )
throw std::bad_alloc();
defragment( newBufferMem );
// Switch to the new vertex buffer
free( m_vertices );
m_vertices = newBufferMem;
#ifdef KICAD_GAL_PROFILE
totalTime.Stop();
wxLogTrace( traceGalCachedContainer, "Defragmented container storing %d vertices / %.1f ms",
m_currentSize - m_freeSpace, totalTime.msecs() );
#endif /* KICAD_GAL_PROFILE */
m_freeSpace += ( aNewSize - m_currentSize );
m_currentSize = aNewSize;
// Now there is only one big chunk of free memory
m_freeChunks.clear();
m_freeChunks.insert( std::make_pair( m_freeSpace, m_currentSize - m_freeSpace ) );
m_dirty = true;
return true;
}

View file

@ -1,86 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright 2013-2017 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef CACHED_CONTAINER_RAM_H_
#define CACHED_CONTAINER_RAM_H_
#include "cached_container.h"
#include <map>
#include <set>
namespace KIGFX
{
class VERTEX_ITEM;
class SHADER;
/**
* Specialization of CACHED_CONTAINER that stores data in RAM.
*
* This is mainly for video cards/drivers that do not cope well with video memory mapping.
*/
class CACHED_CONTAINER_RAM : public CACHED_CONTAINER
{
public:
CACHED_CONTAINER_RAM( unsigned int aSize = DEFAULT_SIZE );
~CACHED_CONTAINER_RAM();
///< @copydoc VERTEX_CONTAINER::Unmap()
void Map() override {}
///< @copydoc VERTEX_CONTAINER::Unmap()
void Unmap() override;
bool IsMapped() const override
{
return true;
}
/**
* Return handle to the vertex buffer.
*
* It might be negative if the buffer is not initialized.
*/
unsigned int GetBufferHandle() const override
{
return m_verticesBuffer; // make common with CACHED_CONTAINER_RAM
}
protected:
/**
* Defragment the currently stored data and resizes the buffer.
*
* @param aNewSize is the new buffer vertex buffer size, expressed as the number of vertices.
* @return true on success.
*/
bool defragmentResize( unsigned int aNewSize ) override;
///< Handle to vertices buffer
GLuint m_verticesBuffer;
};
} // namespace KIGFX
#endif /* CACHED_CONTAINER_RAM_H_ */

View file

@ -1,178 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "fullscreen_quad.h"
#include "utils.h"
using namespace KIGFX;
FULLSCREEN_QUAD::FULLSCREEN_QUAD() :
m_initialized( false ),
m_quadVBO( 0 ),
m_quadVAO( 0 ),
m_triangleVBO( 0 ),
m_triangleVAO( 0 )
{
}
FULLSCREEN_QUAD::~FULLSCREEN_QUAD()
{
Cleanup();
}
void FULLSCREEN_QUAD::Initialize()
{
if( m_initialized )
return;
// Quad vertices: 2 triangles covering -1 to +1 in clip space
// Each vertex has: x, y, z, w (position) + s, t, 0, 0 (texcoord)
// Note: z=0, w=1 for positions; texcoords map [0,1] to screen
static const float quadVertices[] = {
// First triangle (top-left, bottom-left, top-right)
// Position (x,y,z,w) TexCoord (s,t,0,0)
-1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, // top-left
-1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-left
1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, // top-right
// Second triangle (top-right, bottom-left, bottom-right)
1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, // top-right
-1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // bottom-left
1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, // bottom-right
};
// Create quad VAO and VBO
glGenVertexArrays( 1, &m_quadVAO );
glGenBuffers( 1, &m_quadVBO );
glBindVertexArray( m_quadVAO );
glBindBuffer( GL_ARRAY_BUFFER, m_quadVBO );
glBufferData( GL_ARRAY_BUFFER, sizeof( quadVertices ), quadVertices, GL_STATIC_DRAW );
// Position attribute (a_vertex) - location 0
glVertexAttribPointer( VERTEX_ATTRIB_LOC, 4, GL_FLOAT, GL_FALSE, 8 * sizeof( float ),
(void*) 0 );
glEnableVertexAttribArray( VERTEX_ATTRIB_LOC );
// TexCoord attribute (a_texCoord0) - location 1
glVertexAttribPointer( TEXCOORD_ATTRIB_LOC, 4, GL_FLOAT, GL_FALSE, 8 * sizeof( float ),
(void*) ( 4 * sizeof( float ) ) );
glEnableVertexAttribArray( TEXCOORD_ATTRIB_LOC );
glBindVertexArray( 0 );
checkGlError( "creating fullscreen quad VBO", __FILE__, __LINE__ );
// Oversized triangle vertices: covers entire screen with one triangle
// Uses coordinates that extend beyond the viewport
static const float triangleVertices[] = {
// Position (x,y,z,w) TexCoord (s,t,0,0)
-1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, // top-left
-1.0f, -3.0f, 0.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, // bottom-left (extended)
3.0f, 1.0f, 0.0f, 1.0f, 2.0f, 1.0f, 0.0f, 0.0f, // top-right (extended)
};
// Create triangle VAO and VBO
glGenVertexArrays( 1, &m_triangleVAO );
glGenBuffers( 1, &m_triangleVBO );
glBindVertexArray( m_triangleVAO );
glBindBuffer( GL_ARRAY_BUFFER, m_triangleVBO );
glBufferData( GL_ARRAY_BUFFER, sizeof( triangleVertices ), triangleVertices, GL_STATIC_DRAW );
// Position attribute (a_vertex) - location 0
glVertexAttribPointer( VERTEX_ATTRIB_LOC, 4, GL_FLOAT, GL_FALSE, 8 * sizeof( float ),
(void*) 0 );
glEnableVertexAttribArray( VERTEX_ATTRIB_LOC );
// TexCoord attribute (a_texCoord0) - location 1
glVertexAttribPointer( TEXCOORD_ATTRIB_LOC, 4, GL_FLOAT, GL_FALSE, 8 * sizeof( float ),
(void*) ( 4 * sizeof( float ) ) );
glEnableVertexAttribArray( TEXCOORD_ATTRIB_LOC );
glBindVertexArray( 0 );
checkGlError( "creating fullscreen triangle VBO", __FILE__, __LINE__ );
m_initialized = true;
}
void FULLSCREEN_QUAD::Draw()
{
if( !m_initialized )
Initialize();
glBindVertexArray( m_quadVAO );
glDrawArrays( GL_TRIANGLES, 0, 6 );
glBindVertexArray( 0 );
}
void FULLSCREEN_QUAD::DrawTriangle()
{
if( !m_initialized )
Initialize();
glBindVertexArray( m_triangleVAO );
glDrawArrays( GL_TRIANGLES, 0, 3 );
glBindVertexArray( 0 );
}
void FULLSCREEN_QUAD::Cleanup()
{
if( m_quadVBO )
{
glDeleteBuffers( 1, &m_quadVBO );
m_quadVBO = 0;
}
if( m_quadVAO )
{
glDeleteVertexArrays( 1, &m_quadVAO );
m_quadVAO = 0;
}
if( m_triangleVBO )
{
glDeleteBuffers( 1, &m_triangleVBO );
m_triangleVBO = 0;
}
if( m_triangleVAO )
{
glDeleteVertexArrays( 1, &m_triangleVAO );
m_triangleVAO = 0;
}
m_initialized = false;
}
// Global instance
static FULLSCREEN_QUAD s_fullscreenQuad;
FULLSCREEN_QUAD& KIGFX::GetFullscreenQuad()
{
return s_fullscreenQuad;
}

View file

@ -1,97 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file fullscreen_quad.h
* @brief VBO-based fullscreen quad for WebGL texture compositing.
* Replaces legacy GL immediate mode (glBegin/glVertex/glEnd).
*/
#ifndef FULLSCREEN_QUAD_H_
#define FULLSCREEN_QUAD_H_
#include "kiglew.h"
namespace KIGFX
{
/**
* A VBO-based fullscreen quad for drawing textures to the screen.
* Used by compositor and antialiasing passes.
*/
class FULLSCREEN_QUAD
{
public:
FULLSCREEN_QUAD();
~FULLSCREEN_QUAD();
/**
* Initialize the VBO and VAO. Must be called after GL context is created.
*/
void Initialize();
/**
* Draw the fullscreen quad. Assumes a shader is already bound.
* The shader must have:
* - a_vertex (location 0): vec4 position
* - a_texCoord0 (location 1): vec4 texture coordinates
*/
void Draw();
/**
* Draw a fullscreen triangle (more efficient than quad for some GPUs).
* Uses an oversized triangle that covers the entire screen.
*/
void DrawTriangle();
/**
* Check if initialized.
*/
bool IsInitialized() const { return m_initialized; }
/**
* Clean up GL resources.
*/
void Cleanup();
// Attribute locations used by the fullscreen quad
static const GLuint VERTEX_ATTRIB_LOC = 0;
static const GLuint TEXCOORD_ATTRIB_LOC = 1;
private:
bool m_initialized;
GLuint m_quadVBO; ///< VBO for quad vertices (6 vertices, 2 triangles)
GLuint m_quadVAO; ///< VAO for quad
GLuint m_triangleVBO; ///< VBO for single oversized triangle
GLuint m_triangleVAO; ///< VAO for triangle
};
/**
* Get the global fullscreen quad instance.
* This is lazily initialized on first use.
*/
FULLSCREEN_QUAD& GetFullscreenQuad();
} // namespace KIGFX
#endif /* FULLSCREEN_QUAD_H_ */

View file

@ -1,118 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2016 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "gl_context_mgr.h"
#include <wx/debug.h>
wxGLContext* GL_CONTEXT_MANAGER::CreateCtx( wxGLCanvas* aCanvas, const wxGLContext* aOther )
{
wxGLContext* context = new wxGLContext( aCanvas, aOther );
wxCHECK( context, nullptr );
if( !context->IsOK() )
{
delete context;
return nullptr;
}
m_glContexts.insert( std::make_pair( context, aCanvas ) );
return context;
}
void GL_CONTEXT_MANAGER::DestroyCtx( wxGLContext* aContext )
{
if( m_glContexts.count( aContext ) )
{
m_glContexts.erase( aContext );
delete aContext;
}
else
{
// Do not delete unknown GL contexts
wxFAIL;
}
if( m_glCtx == aContext )
m_glCtx = nullptr;
}
void GL_CONTEXT_MANAGER::DeleteAll()
{
m_glCtxMutex.lock();
for( auto& ctx : m_glContexts )
delete ctx.first;
m_glContexts.clear();
m_glCtx = nullptr;
m_glCtxMutex.unlock();
}
void GL_CONTEXT_MANAGER::LockCtx( wxGLContext* aContext, wxGLCanvas* aCanvas )
{
wxCHECK( aContext && m_glContexts.count( aContext ) > 0, /* void */ );
m_glCtxMutex.lock();
wxGLCanvas* canvas = aCanvas ? aCanvas : m_glContexts.at( aContext );
// Prevent assertion failure in wxGLContext::SetCurrent during GAL teardown
#ifdef __WXGTK__
#ifdef KICAD_USE_EGL
if( canvas->GTKGetDrawingWindow() )
#else
if( canvas->GetXWindow() )
#endif // KICAD_USE_EGL
#endif // __WXGTK__
{
canvas->SetCurrent( *aContext );
}
m_glCtx = aContext;
}
void GL_CONTEXT_MANAGER::UnlockCtx( wxGLContext* aContext )
{
wxCHECK( aContext && m_glContexts.count( aContext ) > 0, /* void */ );
if( m_glCtx == aContext )
{
m_glCtxMutex.unlock();
m_glCtx = nullptr;
}
else
{
wxFAIL_MSG( wxString::Format( wxS( "Trying to unlock GL context mutex from "
"a wrong context: aContext %p m_glCtx %p" ), aContext, m_glCtx ) );
}
}

View file

@ -1,147 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2016 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef GL_CONTEXT_MANAGER_H
#define GL_CONTEXT_MANAGER_H
#include <kicommon.h>
#include <gal/gal.h>
#include <wx/glcanvas.h>
#include <mutex>
#include <map>
class KICOMMON_API GL_CONTEXT_MANAGER
{
public:
GL_CONTEXT_MANAGER() : m_glCtx( nullptr ) {}
/**
* Create a managed OpenGL context.
*
* It is assured that the created context is freed upon exit. See wxGLContext
* documentation for the parameters description.
*
* @return Created OpenGL context.
*/
wxGLContext* CreateCtx( wxGLCanvas* aCanvas, const wxGLContext* aOther = nullptr );
/**
* Destroy a managed OpenGL context.
*
* The context to be removed has to be created using GL_CONTEXT_MANAGER::CreateCtx() first.
*
* @param aContext is the OpenGL context to be destroyed. It will not be managed anymore.
*/
void DestroyCtx( wxGLContext* aContext );
/**
* Destroy all managed OpenGL contexts.
*
* This method should be called in the final deinitialization routine.
*/
void DeleteAll();
/**
* Set a context as current and prevents other canvases from switching it.
*
* Requires calling UnlockCtx() when there are no more GL calls for the context. If
* another canvas has already locked a GL context, then the calling process is blocked.
*
* @param aContext is the GL context to be bound.
* @param aCanvas (optional) allows caller to bind the context to a non-parent canvas
* (e.g. when a few canvases share a single GL context).
*/
void LockCtx( wxGLContext* aContext, wxGLCanvas* aCanvas );
/**
* Allow other canvases to bind an OpenGL context.
*
* @param aContext is the currently bound context. It is only a check to assure the right
* canvas wants to unlock GL context.
*/
void UnlockCtx( wxGLContext* aContext );
/**
* Get the currently bound GL context.
*
* @return the currently bound GL context.
*/
wxGLContext* GetCurrentCtx() const
{
return m_glCtx;
}
/**
* Get the currently bound GL canvas.
*
* @return the currently bound GL canvas.
*/
wxGLCanvas* GetCurrentCanvas() const
{
auto it = m_glContexts.find( m_glCtx );
return it != m_glContexts.end() ? it->second : nullptr;
}
/**
* Run the given function first releasing the GL context lock, then restoring it.
*
* @param aFunction is the function to be executed.
*/
template<typename Func, typename... Args>
auto RunWithoutCtxLock( Func&& aFunction, Args&&... args )
{
wxGLContext* currentCtx = GetCurrentCtx();
wxGLCanvas* currentCanvas = GetCurrentCanvas();
UnlockCtx( currentCtx );
if constexpr (std::is_void_v<decltype(aFunction(std::forward<Args>(args)...))>)
{
std::forward<Func>(aFunction)(std::forward<Args>(args)...);
LockCtx( currentCtx, currentCanvas );
return;
}
else
{
auto result = std::forward<Func>(aFunction)(std::forward<Args>(args)...);
LockCtx( currentCtx, currentCanvas );
return result;
}
}
private:
///< Map of GL contexts & their parent canvases.
std::map<wxGLContext*, wxGLCanvas*> m_glContexts;
///< Currently bound GL context.
wxGLContext* m_glCtx;
///< Lock to prevent unexpected GL context switching.
std::mutex m_glCtxMutex;
};
#endif /* GL_CONTEXT_MANAGER_H */

View file

@ -1,63 +0,0 @@
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
// The current font is "Ubuntu Mono" available under Ubuntu Font Licence 1.0
// (see ubuntu-font-licence-1.0.txt for details)
#include <algorithm>
#include "gl_resources.h"
#define BITMAP_FONT_USE_SPANS
namespace KIGFX {
namespace BUILTIN_FONT {
#include "bitmap_font_img.c"
#include "bitmap_font_desc.c"
const FONT_GLYPH_TYPE* LookupGlyph( unsigned int aCodepoint )
{
#ifdef BITMAP_FONT_USE_SPANS
auto *end = font_codepoint_spans + sizeof( font_codepoint_spans ) / sizeof(FONT_SPAN_TYPE);
auto ptr = std::upper_bound( font_codepoint_spans, end, aCodepoint,
[]( unsigned int codepoint, const FONT_SPAN_TYPE& span )
{
return codepoint < span.end;
} );
if( ptr != end && ptr->start <= aCodepoint )
{
unsigned int index = aCodepoint - ptr->start + ptr->cumulative;
return &font_codepoint_infos[ index ];
}
else
{
return nullptr;
}
#else
return &bitmap_chars[codepoint];
#endif
}
}
}

View file

@ -1,73 +0,0 @@
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef GAL_OPENGL_RESOURCES_H___
#define GAL_OPENGL_RESOURCES_H___
#define BITMAP_FONT_USE_SPANS
namespace KIGFX {
namespace BUILTIN_FONT {
struct FONT_IMAGE_TYPE
{
unsigned int width, height;
unsigned int char_border;
unsigned int spacing;
unsigned char pixels[1024 * 1024 * 3];
};
struct FONT_INFO_TYPE
{
unsigned int smooth_pixels;
float min_y;
float max_y;
};
struct FONT_SPAN_TYPE
{
unsigned int start;
unsigned int end;
unsigned int cumulative;
};
struct FONT_GLYPH_TYPE
{
unsigned int atlas_x, atlas_y;
unsigned int atlas_w, atlas_h;
float minx, maxx;
float miny, maxy;
float advance;
};
extern FONT_IMAGE_TYPE font_image;
extern FONT_INFO_TYPE font_information;
const FONT_GLYPH_TYPE* LookupGlyph( unsigned int aCodePoint );
}
}
#endif

View file

@ -1,172 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright The KiCad Developers, see AUTHORS.txt for contributors
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef GL_UTILS_H
#define GL_UTILS_H
#include "kiglew.h" // Must be included first
#include <wx/glcanvas.h>
#include <wx/utils.h>
#ifdef _WIN32
#ifdef __MINGW32__
#pragma GCC push_options
#pragma GCC optimize( "O0" )
#else
#pragma optimize( "", off )
#endif
#endif
class GL_UTILS
{
public:
/**
* Attempt to set the OpenGL swap interval.
*
* @param aVal if -1 = try to set adaptive swapping, 0 = sync off, 1 = sync with VSYNC rate.
* @return actual value set
*/
static int SetSwapInterval( int aVal )
{
#if defined( __linux__ ) && !defined( KICAD_USE_EGL )
if( Display* dpy = glXGetCurrentDisplay() )
{
GLXDrawable drawable = glXGetCurrentDrawable();
std::string exts( glXQueryExtensionsString( dpy, DefaultScreen( dpy ) ) );
if( glXSwapIntervalEXT && glXQueryDrawable && drawable
&& exts.find( "GLX_EXT_swap_control" ) != std::string::npos )
{
if( aVal == -1 )
{
if( exts.find( "GLX_EXT_swap_control_tear" ) == std::string::npos )
{
aVal = 1;
}
else
{
// Even though the extensions might be available,
// we need to be sure that late/adaptive swaps are
// enabled on the drawable.
unsigned lateSwapsEnabled = 0;
glXQueryDrawable( dpy, drawable, GLX_LATE_SWAPS_TEAR_EXT,
&lateSwapsEnabled );
if( !lateSwapsEnabled )
{
aVal = 0;
}
}
}
unsigned clampedInterval;
glXSwapIntervalEXT( dpy, drawable, aVal );
glXQueryDrawable( dpy, drawable, GLX_SWAP_INTERVAL_EXT, &clampedInterval );
return clampedInterval;
}
if( glXSwapIntervalMESA && glXGetSwapIntervalMESA
&& exts.find( "GLX_MESA_swap_control" ) != std::string::npos )
{
if( aVal == -1 )
aVal = 1;
if( !glXSwapIntervalMESA( aVal ) )
return aVal;
}
if( glXSwapIntervalSGI && exts.find( "GLX_SGI_swap_control" ) != std::string::npos )
{
if( aVal == -1 )
aVal = 1;
if( !glXSwapIntervalSGI( aVal ) )
return aVal;
}
}
#elif defined( _WIN32 )
const GLubyte* vendor = glGetString( GL_VENDOR );
const GLubyte* version = glGetString( GL_VERSION );
if( wglSwapIntervalEXT && wxGLCanvas::IsExtensionSupported( "WGL_EXT_swap_control" ) )
{
wxString vendorStr = vendor;
wxString versionStr = version;
if( aVal == -1 && ( !wxGLCanvas::IsExtensionSupported( "WGL_EXT_swap_control_tear" ) ) )
aVal = 1;
// Trying to enable adaptive swapping on AMD drivers from 2017 or older leads to crash
if( aVal == -1 && vendorStr == wxS( "ATI Technologies Inc." ) )
{
wxArrayString parts = wxSplit( versionStr.AfterLast( ' ' ), '.', 0 );
if( parts.size() == 4 )
{
long majorVer = 0;
if( parts[0].ToLong( &majorVer ) )
{
if( majorVer <= 22 )
aVal = 1;
}
}
}
HDC hdc = wglGetCurrentDC();
HGLRC hglrc = wglGetCurrentContext();
if( hdc && hglrc )
{
int currentInterval = wglGetSwapIntervalEXT();
if( currentInterval != aVal )
{
wglSwapIntervalEXT( aVal );
currentInterval = wglGetSwapIntervalEXT();
}
return currentInterval;
}
}
#endif
return 0;
}
};
#ifdef _WIN32
#ifdef __MINGW32__
#pragma GCC pop_options
#else
#pragma optimize( "", on )
#endif
#endif
#endif /* GL_CONTEXT_MANAGER_H */

View file

@ -1,379 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright 2013-2017 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "gpu_manager.h"
#include "cached_container_gpu.h"
#include "cached_container_ram.h"
#include "noncached_container.h"
#include "shader.h"
#include "utils.h"
#include "vertex_item.h"
#include <core/profile.h>
#include <typeinfo>
#include <confirm.h>
#include <trace_helpers.h>
#ifdef KICAD_GAL_PROFILE
#include <core/profile.h>
#include <wx/log.h>
#endif /* KICAD_GAL_PROFILE */
using namespace KIGFX;
GPU_MANAGER* GPU_MANAGER::MakeManager( VERTEX_CONTAINER* aContainer )
{
if( aContainer->IsCached() )
return new GPU_CACHED_MANAGER( aContainer );
else
return new GPU_NONCACHED_MANAGER( aContainer );
}
GPU_MANAGER::GPU_MANAGER( VERTEX_CONTAINER* aContainer ) :
m_isDrawing( false ),
m_container( aContainer ),
m_shader( nullptr ),
m_shaderAttrib( 0 ),
m_vertexAttrib( 0 ),
m_colorAttrib( 0 ),
m_enableDepthTest( true ),
m_vao( 0 )
{
}
GPU_MANAGER::~GPU_MANAGER()
{
// Delete VAO if it was created
if( m_vao != 0 )
{
glDeleteVertexArrays( 1, &m_vao );
m_vao = 0;
}
}
void GPU_MANAGER::SetShader( SHADER& aShader )
{
m_shader = &aShader;
m_shaderAttrib = m_shader->GetAttribute( "a_shaderParams" );
m_vertexAttrib = m_shader->GetAttribute( "a_vertex" );
m_colorAttrib = m_shader->GetAttribute( "a_color" );
if( m_shaderAttrib == -1 )
{
DisplayError( nullptr, wxT( "Could not get the shader attribute location" ) );
}
// Create VAO for WebGL 2.0 / OpenGL ES 3.0 compatibility
// WebGL 2.0 requires a VAO to be bound before calling glVertexAttribPointer
if( m_vao == 0 )
{
glGenVertexArrays( 1, &m_vao );
}
}
// Cached manager
GPU_CACHED_MANAGER::GPU_CACHED_MANAGER( VERTEX_CONTAINER* aContainer ) :
GPU_MANAGER( aContainer ),
m_buffersInitialized( false ),
m_indicesCapacity( 0 ),
m_totalHuge( 0 ),
m_totalNormal( 0 ),
m_indexBufSize( 0 ),
m_indexBufMaxSize( 0 ),
m_curVrangeSize( 0 )
{
}
GPU_CACHED_MANAGER::~GPU_CACHED_MANAGER()
{
}
void GPU_CACHED_MANAGER::BeginDrawing()
{
wxASSERT( !m_isDrawing );
m_curVrangeSize = 0;
m_indexBufMaxSize = 0;
m_indexBufSize = 0;
m_vranges.clear();
m_isDrawing = true;
}
void GPU_CACHED_MANAGER::DrawIndices( const VERTEX_ITEM* aItem )
{
// Hot path: don't use wxASSERT
assert( m_isDrawing );
unsigned int offset = aItem->GetOffset();
unsigned int size = aItem->GetSize();
if( size == 0 )
return;
if( size <= 1000 )
{
m_totalNormal += size;
m_vranges.emplace_back( offset, offset + size - 1, false );
m_curVrangeSize += size;
}
else
{
m_totalHuge += size;
m_vranges.emplace_back( offset, offset + size - 1, true );
m_indexBufSize = std::max( m_curVrangeSize, m_indexBufSize );
m_curVrangeSize = 0;
}
}
void GPU_CACHED_MANAGER::EndDrawing()
{
wxASSERT( m_isDrawing );
CACHED_CONTAINER* cached = static_cast<CACHED_CONTAINER*>( m_container );
if( cached->IsMapped() )
cached->Unmap();
m_indexBufSize = std::max( m_curVrangeSize, m_indexBufSize );
m_indexBufMaxSize = std::max( 2*m_indexBufSize, m_indexBufMaxSize );
resizeIndices( m_indexBufMaxSize );
if( m_enableDepthTest )
glEnable( GL_DEPTH_TEST );
else
glDisable( GL_DEPTH_TEST );
// Bind VAO first (required for WebGL 2.0 / OpenGL ES 3.0)
glBindVertexArray( m_vao );
// Bind vertices data buffers
glBindBuffer( GL_ARRAY_BUFFER, cached->GetBufferHandle() );
// Modern vertex attributes (replacing legacy glEnableClientState/glVertexPointer/glColorPointer)
// Vertex position (a_vertex)
glEnableVertexAttribArray( m_vertexAttrib );
glVertexAttribPointer( m_vertexAttrib, COORD_STRIDE, GL_FLOAT, GL_FALSE, VERTEX_SIZE,
(GLvoid*) COORD_OFFSET );
// Vertex color (a_color) - note: normalize=GL_TRUE for unsigned bytes to [0,1]
glEnableVertexAttribArray( m_colorAttrib );
glVertexAttribPointer( m_colorAttrib, COLOR_STRIDE, GL_UNSIGNED_BYTE, GL_TRUE, VERTEX_SIZE,
(GLvoid*) COLOR_OFFSET );
if( m_shader != nullptr ) // Use shader if applicable
{
m_shader->Use();
glEnableVertexAttribArray( m_shaderAttrib );
glVertexAttribPointer( m_shaderAttrib, SHADER_STRIDE, GL_FLOAT, GL_FALSE, VERTEX_SIZE,
(GLvoid*) SHADER_OFFSET );
}
PROF_TIMER cntDraw( "gl-draw-elements" );
int n_ranges = m_vranges.size();
int n = 0;
GLuint* iptr = m_indices.get();
GLuint icnt = 0;
int drawCalls = 0;
while( n < n_ranges )
{
VRANGE* cur = &m_vranges[n];
if( cur->m_isContinuous )
{
if( icnt > 0 )
{
glDrawElements( GL_TRIANGLES, icnt, GL_UNSIGNED_INT, m_indices.get() );
drawCalls++;
}
icnt = 0;
iptr = m_indices.get();
glDrawArrays( GL_TRIANGLES, cur->m_start, cur->m_end - cur->m_start + 1 );
drawCalls++;
}
else
{
for( GLuint i = cur->m_start; i <= cur->m_end; i++ )
{
*iptr++ = i;
icnt++;
}
}
n++;
}
if( icnt > 0 )
{
glDrawElements( GL_TRIANGLES, icnt, GL_UNSIGNED_INT, m_indices.get() );
drawCalls++;
}
cntDraw.Stop();
KI_TRACE( traceGalProfile,
"Cached manager size: VBO size %u iranges %zu max elt size %u drawcalls %u\n",
cached->AllItemsSize(), m_vranges.size(), m_indexBufMaxSize, drawCalls );
KI_TRACE( traceGalProfile, "Timing: %s\n", cntDraw.to_string() );
glBindBuffer( GL_ARRAY_BUFFER, 0 );
cached->ClearDirty();
// Deactivate vertex arrays (modern vertex attributes)
glDisableVertexAttribArray( m_colorAttrib );
glDisableVertexAttribArray( m_vertexAttrib );
if( m_shader != nullptr )
{
glDisableVertexAttribArray( m_shaderAttrib );
m_shader->Deactivate();
}
// Unbind VAO
glBindVertexArray( 0 );
m_isDrawing = false;
}
void GPU_CACHED_MANAGER::resizeIndices( unsigned int aNewSize )
{
if( aNewSize > m_indicesCapacity )
{
m_indicesCapacity = aNewSize;
m_indices.reset( new GLuint[m_indicesCapacity] );
}
}
// Noncached manager
GPU_NONCACHED_MANAGER::GPU_NONCACHED_MANAGER( VERTEX_CONTAINER* aContainer ) :
GPU_MANAGER( aContainer )
{
}
void GPU_NONCACHED_MANAGER::BeginDrawing()
{
// Nothing has to be prepared
}
void GPU_NONCACHED_MANAGER::DrawIndices( const VERTEX_ITEM* aItem )
{
wxASSERT_MSG( false, wxT( "Not implemented yet" ) );
}
void GPU_NONCACHED_MANAGER::EndDrawing()
{
#ifdef KICAD_GAL_PROFILE
PROF_TIMER totalRealTime;
#endif /* KICAD_GAL_PROFILE */
if( m_container->GetSize() == 0 )
return;
VERTEX* vertices = m_container->GetAllVertices();
GLfloat* coordinates = (GLfloat*) ( vertices );
GLubyte* colors = (GLubyte*) ( vertices ) + COLOR_OFFSET;
if( m_enableDepthTest )
glEnable( GL_DEPTH_TEST );
else
glDisable( GL_DEPTH_TEST );
// Bind VAO first (required for WebGL 2.0 / OpenGL ES 3.0)
glBindVertexArray( m_vao );
// Modern vertex attributes (replacing legacy glEnableClientState/glVertexPointer/glColorPointer)
// Vertex position (a_vertex)
glEnableVertexAttribArray( m_vertexAttrib );
glVertexAttribPointer( m_vertexAttrib, COORD_STRIDE, GL_FLOAT, GL_FALSE, VERTEX_SIZE,
coordinates );
// Vertex color (a_color) - note: normalize=GL_TRUE for unsigned bytes to [0,1]
glEnableVertexAttribArray( m_colorAttrib );
glVertexAttribPointer( m_colorAttrib, COLOR_STRIDE, GL_UNSIGNED_BYTE, GL_TRUE, VERTEX_SIZE,
colors );
if( m_shader != nullptr ) // Use shader if applicable
{
GLfloat* shaders = (GLfloat*) ( vertices ) + SHADER_OFFSET / sizeof( GLfloat );
m_shader->Use();
glEnableVertexAttribArray( m_shaderAttrib );
glVertexAttribPointer( m_shaderAttrib, SHADER_STRIDE, GL_FLOAT, GL_FALSE, VERTEX_SIZE,
shaders );
}
glDrawArrays( GL_TRIANGLES, 0, m_container->GetSize() );
#ifdef KICAD_GAL_PROFILE
wxLogTrace( traceGalProfile, wxT( "Noncached manager size: %d" ), m_container->GetSize() );
#endif /* KICAD_GAL_PROFILE */
// Deactivate vertex arrays
glDisableVertexAttribArray( m_colorAttrib );
glDisableVertexAttribArray( m_vertexAttrib );
if( m_shader != nullptr )
{
glDisableVertexAttribArray( m_shaderAttrib );
m_shader->Deactivate();
}
// Unbind VAO
glBindVertexArray( 0 );
m_container->Clear();
#ifdef KICAD_GAL_PROFILE
totalRealTime.Stop();
wxLogTrace( traceGalProfile, wxT( "GPU_NONCACHED_MANAGER::EndDrawing(): %.1f ms" ),
totalRealTime.msecs() );
#endif /* KICAD_GAL_PROFILE */
}
void GPU_MANAGER::EnableDepthTest( bool aEnabled )
{
m_enableDepthTest = aEnabled;
}

View file

@ -1,194 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef GPU_MANAGER_H_
#define GPU_MANAGER_H_
#include <vector>
#include "vertex_common.h"
#include <boost/scoped_array.hpp>
namespace KIGFX
{
class SHADER;
class VERTEX_CONTAINER;
class VERTEX_ITEM;
class CACHED_CONTAINER;
class NONCACHED_CONTAINER;
/**
* Class to handle uploading vertices and indices to GPU in drawing purposes.
*/
class GPU_MANAGER
{
public:
static GPU_MANAGER* MakeManager( VERTEX_CONTAINER* aContainer );
virtual ~GPU_MANAGER();
/**
* Prepare the stored data to be drawn.
*/
virtual void BeginDrawing() = 0;
/**
* Make the GPU draw given range of vertices.
*
* @param aOffset is the beginning of the range.
* @param aSize is the number of vertices to be drawn.
*/
virtual void DrawIndices( const VERTEX_ITEM* aItem ) = 0;
/**
* Clear the container after drawing routines.
*/
virtual void EndDrawing() = 0;
/**
* Allow using shaders with the stored data.
*
* @param aShader is the object that allows using shaders.
*/
virtual void SetShader( SHADER& aShader );
/**
* Enable/disable Z buffer depth test.
*/
void EnableDepthTest( bool aEnabled );
protected:
GPU_MANAGER( VERTEX_CONTAINER* aContainer );
///< Drawing status flag.
bool m_isDrawing;
///< Container that stores vertices data.
VERTEX_CONTAINER* m_container;
///< Shader handling
SHADER* m_shader;
///< Location of shader attributes (for glVertexAttribPointer)
int m_shaderAttrib;
int m_vertexAttrib; ///< Location of a_vertex attribute
int m_colorAttrib; ///< Location of a_color attribute
///< true: enable Z test when drawing
bool m_enableDepthTest;
///< VAO for WebGL 2.0 / OpenGL ES 3.0 compatibility
///< WebGL 2.0 requires a VAO to be bound before setting vertex attributes
unsigned int m_vao;
};
class GPU_CACHED_MANAGER : public GPU_MANAGER
{
public:
struct VRANGE
{
VRANGE( int aStart, int aEnd, bool aContinuous ) :
m_start( aStart ),
m_end( aEnd ),
m_isContinuous( aContinuous )
{
}
unsigned int m_start, m_end;
bool m_isContinuous;
};
GPU_CACHED_MANAGER( VERTEX_CONTAINER* aContainer );
~GPU_CACHED_MANAGER();
///< @copydoc GPU_MANAGER::BeginDrawing()
virtual void BeginDrawing() override;
///< @copydoc GPU_MANAGER::DrawIndices()
virtual void DrawIndices( const VERTEX_ITEM* aItem ) override;
///< @copydoc GPU_MANAGER::EndDrawing()
virtual void EndDrawing() override;
///< Map vertex buffer stored in GPU memory.
void Map();
///< Unmap vertex buffer.
void Unmap();
protected:
///< Resizes the indices buffer to aNewSize if necessary
void resizeIndices( unsigned int aNewSize );
///< Buffers initialization flag
bool m_buffersInitialized;
///< Pointer to the current indices buffer
boost::scoped_array<GLuint> m_indices;
///< Current indices buffer size
unsigned int m_indicesCapacity;
///< Ranges of visible vertex indices to render
std::vector<VRANGE> m_vranges;
///< Number of huge VRANGEs (i.e. large zones) with separate draw calls
int m_totalHuge;
///< Number of regular VRANGEs (small items) pooled into single draw call
int m_totalNormal;
///< Current size of index buffer
unsigned int m_indexBufSize;
///< Maximum size taken by the index buffer for all frames rendered so far
unsigned int m_indexBufMaxSize;
///< Size of the current VRANGE
unsigned int m_curVrangeSize;
};
class GPU_NONCACHED_MANAGER : public GPU_MANAGER
{
public:
GPU_NONCACHED_MANAGER( VERTEX_CONTAINER* aContainer );
///< @copydoc GPU_MANAGER::BeginDrawing()
virtual void BeginDrawing() override;
///< @copydoc GPU_MANAGER::DrawIndices()
virtual void DrawIndices( const VERTEX_ITEM* aItem ) override;
///< @copydoc GPU_MANAGER::EndDrawing()
virtual void EndDrawing() override;
};
} // namespace KIGFX
#endif /* GPU_MANAGER_H_ */

View file

@ -1,203 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* This file is used for including the proper GLEW header for the platform.
*/
#ifndef KIGLEW_H_
#define KIGLEW_H_
// Pull in the configuration options for wxWidgets
#include <wx/platform.h>
#if defined( __EMSCRIPTEN__ )
// Prevent real GLEW header from being included (Emscripten has one too)
#ifndef __glew_h__
#define __glew_h__
#endif
// WebGL2/GLES3: Modern shader functions
// Note: NO legacy GL includes - all rendering uses VBOs/shaders
#include <GLES3/gl3.h>
// GLU tesselator - provided by wasm/stubs/glu_wasm_impl.cpp
#include <GL/glu.h>
// GLEW compatibility stubs for WebGL
#define GLEW_OK 0
#define GLEW_VERSION 1
#define GLEW_VERSION_1_2 1
#define GLEW_VERSION_1_3 1
#define GLEW_VERSION_1_4 1
#define GLEW_VERSION_1_5 1
#define GLEW_VERSION_2_0 1
#define GLEW_VERSION_2_1 1
#define GLEW_ARB_vertex_array_object 1
#define GLEW_ARB_vertex_buffer_object 1
#define GLEW_ARB_framebuffer_object 1
#define GLEW_EXT_framebuffer_object 1
#define GLEW_ARB_texture_non_power_of_two 1
#define GLEW_ARB_copy_buffer 0 // Not available in WebGL 1.0
#define GLEW_EXT_framebuffer_multisample 0 // Limited in WebGL
inline int glewInit() { return GLEW_OK; }
inline const unsigned char* glewGetString(int) { return (const unsigned char*)"WebGL"; }
inline const char* glewGetErrorString(int) { return ""; }
inline int glewIsSupported(const char*) { return 1; }
// VAO functions - available in WebGL2 / OpenGL ES 3.0
#ifndef GL_VERTEX_ARRAY_BINDING
#define GL_VERTEX_ARRAY_BINDING 0x85B5
#endif
// Geometry shader extensions - not supported in WebGL
#ifndef GL_GEOMETRY_VERTICES_OUT_EXT
#define GL_GEOMETRY_VERTICES_OUT_EXT 0x8DDA
#define GL_GEOMETRY_INPUT_TYPE_EXT 0x8DDB
#define GL_GEOMETRY_OUTPUT_TYPE_EXT 0x8DDC
#endif
// Geometry shader function stub (not supported in WebGL)
inline void glProgramParameteriEXT(GLuint program, GLenum pname, GLint value) {
(void)program; (void)pname; (void)value;
}
// glMapBuffer family - not available in WebGL 1.0
// Return nullptr to signal failure, KiCad has RAM-based fallback
inline void* glMapBuffer(GLenum target, GLenum access) {
(void)target; (void)access;
return nullptr;
}
inline GLboolean glUnmapBuffer(GLenum target) {
(void)target;
return GL_TRUE;
}
// Buffer copy - not available in WebGL 1.0, no-op stub
inline void glCopyBufferSubData(GLenum readTarget, GLenum writeTarget,
GLintptr readOffset, GLintptr writeOffset,
GLsizeiptr size) {
(void)readTarget; (void)writeTarget;
(void)readOffset; (void)writeOffset; (void)size;
}
// EXT framebuffer functions - alias to standard GL ES 2.0 functions
#ifndef GL_FRAMEBUFFER_EXT
#define GL_FRAMEBUFFER_EXT GL_FRAMEBUFFER
#endif
#ifndef GL_RENDERBUFFER_EXT
#define GL_RENDERBUFFER_EXT GL_RENDERBUFFER
#endif
#ifndef GL_FRAMEBUFFER_COMPLETE_EXT
#define GL_FRAMEBUFFER_COMPLETE_EXT GL_FRAMEBUFFER_COMPLETE
#endif
#define glGenFramebuffersEXT glGenFramebuffers
#define glDeleteFramebuffersEXT glDeleteFramebuffers
#define glBindFramebufferEXT glBindFramebuffer
#define glCheckFramebufferStatusEXT glCheckFramebufferStatus
#define glFramebufferTexture2DEXT glFramebufferTexture2D
#define glFramebufferRenderbufferEXT glFramebufferRenderbuffer
#define glGenRenderbuffersEXT glGenRenderbuffers
#define glDeleteRenderbuffersEXT glDeleteRenderbuffers
#define glBindRenderbufferEXT glBindRenderbuffer
#define glRenderbufferStorageEXT glRenderbufferStorage
// GL_DEPTH24_STENCIL8 - map to GLES2/WebGL constant
#ifndef GL_DEPTH24_STENCIL8
#define GL_DEPTH24_STENCIL8 0x88F0
#endif
// GL_DEPTH_STENCIL_ATTACHMENT - WebGL uses separate depth/stencil, but this constant exists
#ifndef GL_DEPTH_STENCIL_ATTACHMENT
#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
#endif
// Debug output - not available in WebGL, no-op stubs
#ifndef GL_DEBUG_OUTPUT
#define GL_DEBUG_OUTPUT 0x92E0
#endif
#ifndef GLchar
typedef char GLchar;
#endif
typedef void (*GLDEBUGPROC)(GLenum source, GLenum type, GLuint id,
GLenum severity, GLsizei length,
const GLchar* message, const void* userParam);
inline void glDebugMessageCallback(GLDEBUGPROC callback, const void* userParam) {
(void)callback; (void)userParam;
}
// GLdouble type (needed for some API signatures)
#ifndef GLdouble
typedef double GLdouble;
#endif
// Display lists - not supported in WebGL, stub implementations
inline GLuint glGenLists(GLsizei range) { (void)range; return 0; }
inline GLboolean glIsList(GLuint list) { (void)list; return GL_FALSE; }
inline void glNewList(GLuint list, GLenum mode) { (void)list; (void)mode; }
inline void glEndList(void) {}
inline void glCallList(GLuint list) { (void)list; }
inline void glDeleteLists(GLuint list, GLsizei range) { (void)list; (void)range; }
#elif defined( __unix__ ) and not defined( __APPLE__ )
#ifdef KICAD_USE_EGL
#if wxUSE_GLCANVAS_EGL
// wxWidgets was compiled with the EGL canvas, so use the EGL header for GLEW
#include <GL/eglew.h>
#else
#error "KICAD_USE_EGL can only be used when wxWidgets is compiled with the EGL canvas"
#endif
#else // KICAD_USE_EGL
#if wxUSE_GLCANVAS_EGL
#error "KICAD_USE_EGL must be defined since wxWidgets has been compiled with the EGL canvas"
#else
// wxWidgets wasn't compiled with the EGL canvas, so use the X11 GLEW
#include <GL/glxew.h>
#endif
#endif // KICAD_USE_EGL
#else // defined( __unix__ ) and not defined( __APPLE__ )
// Non-GTK platforms only need the normal GLEW include
#include <GL/glew.h>
#endif // defined( __unix__ ) and not defined( __APPLE__ )
#ifdef _WIN32
#include <GL/wglew.h>
#endif // _WIN32
#endif // KIGLEW_H_

View file

@ -1,102 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file noncached_container.cpp
* @brief Class to store instances of VERTEX without caching. It allows a fast one-frame drawing
* and then clearing the buffer and starting from scratch.
*/
#include "noncached_container.h"
#include <cstring>
#include <cstdlib>
using namespace KIGFX;
NONCACHED_CONTAINER::NONCACHED_CONTAINER( unsigned int aSize ) :
VERTEX_CONTAINER( aSize ),
m_freePtr( 0 )
{
m_vertices = static_cast<VERTEX*>( malloc( aSize * sizeof( VERTEX ) ) );
// Unfortunately we cannot remove the use of malloc here because realloc is used in
// the Allocate method below. The new operator behavior is mimicked here so that a
// malloc failure can be caught in the OpenGL initialization code further up the stack.
if( !m_vertices )
throw std::bad_alloc();
memset( m_vertices, 0x00, aSize * sizeof( VERTEX ) );
}
NONCACHED_CONTAINER::~NONCACHED_CONTAINER()
{
free( m_vertices );
}
void NONCACHED_CONTAINER::SetItem( VERTEX_ITEM* aItem )
{
// Nothing has to be done, as the noncached container
// does not care about VERTEX_ITEMs ownership
}
VERTEX* NONCACHED_CONTAINER::Allocate( unsigned int aSize )
{
if( m_freeSpace < aSize )
{
// Double the space
VERTEX* newVertices =
static_cast<VERTEX*>( realloc( m_vertices, m_currentSize * 2 * sizeof( VERTEX ) ) );
if( newVertices != nullptr )
{
m_vertices = newVertices;
m_freeSpace += m_currentSize;
m_currentSize *= 2;
}
else
{
throw std::bad_alloc();
}
}
VERTEX* freeVertex = &m_vertices[m_freePtr];
// Move to the next free chunk
m_freePtr += aSize;
m_freeSpace -= aSize;
return freeVertex;
}
void NONCACHED_CONTAINER::Clear()
{
m_freePtr = 0;
m_freeSpace = m_currentSize;
}

View file

@ -1,85 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file noncached_container.h
* @brief Class to store instances of VERTEX without caching. It allows a fast one-frame drawing
* and then clearing the buffer and starting from scratch.
*/
#ifndef NONCACHED_CONTAINER_H_
#define NONCACHED_CONTAINER_H_
#include "vertex_container.h"
namespace KIGFX
{
class VERTEX_ITEM;
class SHADER;
class NONCACHED_CONTAINER : public VERTEX_CONTAINER
{
public:
/**
* Construct a non-cached container object.
*
* @param aSize is the size of the cache.
* @throw bad_alloc exception if memory allocation fails.
*/
NONCACHED_CONTAINER( unsigned int aSize = DEFAULT_SIZE );
virtual ~NONCACHED_CONTAINER();
bool IsCached() const override
{
return false;
}
/// @copydoc VERTEX_CONTAINER::SetItem( VERTEX_ITEM* aItem )
virtual void SetItem( VERTEX_ITEM* aItem ) override;
/// @copydoc VERTEX_CONTAINER::Allocate( unsigned int aSize )
virtual VERTEX* Allocate( unsigned int aSize ) override;
/// @copydoc VERTEX_CONTAINER::Delete( VERTEX_ITEM* aItem )
void Delete( VERTEX_ITEM* aItem ) override {}
/// @copydoc VERTEX_CONTAINER::Clear()
virtual void Clear() override;
/// @copydoc VERTEX_CONTAINER::GetSize()
virtual unsigned int GetSize() const override
{
// As the m_freePtr points to the first free space, we can safely assume
// that this is the number of vertices stored inside
return m_freePtr;
}
protected:
///< Index of the free first space where a vertex can be stored
unsigned int m_freePtr;
};
} // namespace KIGFX
#endif /* NONCACHED_CONTAINER_H_ */

View file

@ -1,305 +0,0 @@
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* Graphics Abstraction Layer (GAL) for OpenGL
*
* Shader class
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <iostream>
#include <fstream>
#include <stdexcept>
#include <cstring>
#include <cassert>
#include "shader.h"
#include <vector>
using namespace KIGFX;
SHADER::SHADER() :
isProgramCreated( false ),
isShaderLinked( false ),
active( false ),
maximumVertices( 4 ),
geomInputType( GL_LINES ),
geomOutputType( GL_LINES )
{
// Do not have uninitialized members:
programNumber = 0;
}
SHADER::~SHADER()
{
if( active )
Deactivate();
if( isProgramCreated )
{
if( glIsShader )
{
// Delete the shaders and the program
for( std::deque<GLuint>::iterator it = shaderNumbers.begin(); it != shaderNumbers.end();
++it )
{
GLuint shader = *it;
if( glIsShader( shader ) )
{
glDetachShader( programNumber, shader );
glDeleteShader( shader );
}
}
glDeleteProgram( programNumber );
}
}
}
bool SHADER::LoadShaderFromFile( SHADER_TYPE aShaderType, const std::string& aShaderSourceName )
{
// Load shader sources
const std::string shaderSource = ReadSource( aShaderSourceName );
return LoadShaderFromStrings( aShaderType, shaderSource );
}
void SHADER::ConfigureGeometryShader( GLuint maxVertices, GLuint geometryInputType,
GLuint geometryOutputType )
{
maximumVertices = maxVertices;
geomInputType = geometryInputType;
geomOutputType = geometryOutputType;
}
bool SHADER::Link()
{
// Shader linking
glLinkProgram( programNumber );
programInfo( programNumber );
// Check the Link state
GLint tmp;
glGetProgramiv( programNumber, GL_LINK_STATUS, &tmp );
isShaderLinked = !!tmp;
#ifdef DEBUG
if( !isShaderLinked )
{
int maxLength;
glGetProgramiv( programNumber, GL_INFO_LOG_LENGTH, &maxLength );
maxLength = maxLength + 1;
char* linkInfoLog = new char[maxLength];
glGetProgramInfoLog( programNumber, maxLength, &maxLength, linkInfoLog );
std::cerr << "Shader linking error:" << std::endl;
std::cerr << linkInfoLog;
delete[] linkInfoLog;
}
#endif /* DEBUG */
return isShaderLinked;
}
int SHADER::AddParameter( const std::string& aParameterName )
{
GLint location = glGetUniformLocation( programNumber, aParameterName.c_str() );
if( location >= 0 )
parameterLocation.push_back( location );
else
throw std::runtime_error( "Could not find shader uniform: " + aParameterName );
return static_cast<int>( parameterLocation.size() ) - 1;
}
void SHADER::SetParameter( int parameterNumber, float value ) const
{
assert( (unsigned) parameterNumber < parameterLocation.size() );
glUniform1f( parameterLocation[parameterNumber], value );
}
void SHADER::SetParameter( int parameterNumber, int value ) const
{
assert( (unsigned) parameterNumber < parameterLocation.size() );
glUniform1i( parameterLocation[parameterNumber], value );
}
void SHADER::SetParameter( int parameterNumber, float f0, float f1, float f2, float f3 ) const
{
assert( (unsigned) parameterNumber < parameterLocation.size() );
float arr[4] = { f0, f1, f2, f3 };
glUniform4fv( parameterLocation[parameterNumber], 1, arr );
}
void SHADER::SetParameter( int aParameterNumber, const VECTOR2D& aValue ) const
{
assert( (unsigned) aParameterNumber < parameterLocation.size() );
glUniform2f( parameterLocation[aParameterNumber], static_cast<GLfloat>( aValue.x ),
static_cast<GLfloat>( aValue.y ) );
}
void SHADER::SetParameter( int aParameterNumber, const float* aMatrix4x4 ) const
{
assert( (unsigned) aParameterNumber < parameterLocation.size() );
glUniformMatrix4fv( parameterLocation[aParameterNumber], 1, GL_FALSE, aMatrix4x4 );
}
int SHADER::GetAttribute( const std::string& aAttributeName ) const
{
return glGetAttribLocation( programNumber, aAttributeName.c_str() );
}
void SHADER::programInfo( GLuint aProgram )
{
GLint glInfoLogLength = 0;
GLint writtenChars = 0;
// Get the length of the info string
glGetProgramiv( aProgram, GL_INFO_LOG_LENGTH, &glInfoLogLength );
// Print the information
if( glInfoLogLength > 2 )
{
GLchar* glInfoLog = new GLchar[glInfoLogLength];
glGetProgramInfoLog( aProgram, glInfoLogLength, &writtenChars, glInfoLog );
delete[] glInfoLog;
}
}
void SHADER::shaderInfo( GLuint aShader )
{
GLint glInfoLogLength = 0;
GLint writtenChars = 0;
// Get the length of the info string
glGetShaderiv( aShader, GL_INFO_LOG_LENGTH, &glInfoLogLength );
// Print the information
if( glInfoLogLength > 2 )
{
GLchar* glInfoLog = new GLchar[glInfoLogLength];
glGetShaderInfoLog( aShader, glInfoLogLength, &writtenChars, glInfoLog );
delete[] glInfoLog;
}
}
std::string SHADER::ReadSource( const std::string& aShaderSourceName )
{
// Open the shader source for reading
std::ifstream inputFile( aShaderSourceName.c_str(), std::ifstream::in );
std::string shaderSource;
if( !inputFile )
throw std::runtime_error( "Can't read the shader source: " + aShaderSourceName );
std::string shaderSourceLine;
// Read all lines from the text file
while( getline( inputFile, shaderSourceLine ) )
{
shaderSource += shaderSourceLine;
shaderSource += "\n";
}
return shaderSource;
}
bool SHADER::loadShaderFromStringArray( SHADER_TYPE aShaderType, const char** aArray, size_t aSize )
{
assert( !isShaderLinked );
// Create the program
if( !isProgramCreated )
{
programNumber = glCreateProgram();
isProgramCreated = true;
}
// Create a shader
GLuint shaderNumber = glCreateShader( aShaderType );
shaderNumbers.push_back( shaderNumber );
// Get the program info
programInfo( programNumber );
// Attach the sources
glShaderSource( shaderNumber, static_cast<GLsizei>( aSize ), (const GLchar**) aArray, nullptr );
programInfo( programNumber );
// Compile and attach shader to the program
glCompileShader( shaderNumber );
GLint status;
glGetShaderiv( shaderNumber, GL_COMPILE_STATUS, &status );
if( status != GL_TRUE )
{
shaderInfo( shaderNumber );
GLint maxLength = 0;
glGetShaderiv( shaderNumber, GL_INFO_LOG_LENGTH, &maxLength );
// The maxLength includes the NULL character
std::vector<GLchar> errorLog( (size_t) maxLength );
glGetShaderInfoLog( shaderNumber, maxLength, &maxLength, &errorLog[0] );
// Provide the infolog in whatever manor you deem best.
// Exit with failure.
glDeleteShader( shaderNumber ); // Don't leak the shader.
throw std::runtime_error( &errorLog[0] );
}
glAttachShader( programNumber, shaderNumber );
programInfo( programNumber );
// Special handling for the geometry shader
if( aShaderType == SHADER_TYPE_GEOMETRY )
{
glProgramParameteriEXT( programNumber, GL_GEOMETRY_VERTICES_OUT_EXT, maximumVertices );
glProgramParameteriEXT( programNumber, GL_GEOMETRY_INPUT_TYPE_EXT, geomInputType );
glProgramParameteriEXT( programNumber, GL_GEOMETRY_OUTPUT_TYPE_EXT, geomOutputType );
}
return true;
}

View file

@ -1,237 +0,0 @@
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* Graphics Abstraction Layer (GAL) for OpenGL
*
* Shader class
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef SHADER_H_
#define SHADER_H_
#include "kiglew.h" // Must be included first
#include <math/vector2d.h>
#include <string>
#include <deque>
namespace KIGFX
{
class WEBGL_GAL;
/// Type definition for the shader
enum SHADER_TYPE
{
SHADER_TYPE_VERTEX = GL_VERTEX_SHADER, ///< Vertex shader
SHADER_TYPE_FRAGMENT = GL_FRAGMENT_SHADER, ///< Fragment shader
SHADER_TYPE_GEOMETRY = GL_GEOMETRY_SHADER ///< Geometry shader
};
namespace DETAIL {
inline const char* translateStringArg( const std::string& str )
{
return str.c_str();
}
inline const char* translateStringArg( const char* str )
{
return str;
}
}
/**
* Provide the access to the OpenGL shaders.
*
* The purpose of this class is advanced drawing with OpenGL. One example is using the pixel
* shader for drawing exact circles or for anti-aliasing. This class supports vertex, geometry
* and fragment shaders.
*
* Make sure that the hardware supports these features. This can be identified with the "GLEW"
* library.
*/
class SHADER
{
public:
SHADER();
virtual ~SHADER();
/**
* Add a shader and compile the shader sources.
*
* @param aArgs is the list of strings (std::string or convertible to const char*) which
* are concatenated and compiled as a single shader source code.
* @param aShaderType is the type of the shader.
* @return True in case of success, false otherwise.
*/
template< typename... Args >
bool LoadShaderFromStrings( SHADER_TYPE aShaderType, Args&&... aArgs )
{
const char* arr[] = { DETAIL::translateStringArg( aArgs )... };
return loadShaderFromStringArray( aShaderType, arr, sizeof...(Args) );
}
/**
* Load one of the built-in shaders and compiles it.
*
* @param aShaderSourceName is the shader source file name.
* @param aShaderType is the type of the shader.
* @return True in case of success, false otherwise.
*/
bool LoadShaderFromFile( SHADER_TYPE aShaderType, const std::string& aShaderSourceName );
/**
* Link the shaders.
*
* @return true in case of success, false otherwise.
*/
bool Link();
/**
* Return true if shaders are linked correctly.
*/
bool IsLinked() const
{
return isShaderLinked;
}
/**
* Use the shader.
*/
inline void Use()
{
glUseProgram( programNumber );
active = true;
}
/**
* Deactivate the shader and use the default OpenGL program.
*/
inline void Deactivate()
{
glUseProgram( 0 );
active = false;
}
/**
* Return the current state of the shader.
*
* @return True if any of shaders is enabled.
*/
inline bool IsActive() const
{
return active;
}
/**
* Configure the geometry shader - has to be done before linking!
*
* @param maxVertices is the maximum of vertices to be generated.
* @param geometryInputType is the input type [e.g. GL_LINES, GL_TRIANGLES, GL_QUADS etc.]
* @param geometryOutputType is the output type [e.g. GL_LINES, GL_TRIANGLES, GL_QUADS etc.]
*/
void ConfigureGeometryShader( GLuint maxVertices, GLuint geometryInputType,
GLuint geometryOutputType );
/**
* Add a parameter to the parameter queue.
*
* To communicate with the shader use this function to set up the names for the uniform
* variables. These are queued in a list and can be assigned with the SetParameter(..)
* method using the queue position.
*
* @param aParameterName is the name of the parameter.
* @return the added parameter location.
*/
int AddParameter( const std::string& aParameterName );
/**
* Set a parameter of the shader.
*
* @param aParameterNumber is the number of the parameter.
* @param aValue is the value of the parameter.
*/
void SetParameter( int aParameterNumber, float aValue ) const;
void SetParameter( int aParameterNumber, int aValue ) const;
void SetParameter( int aParameterNumber, const VECTOR2D& aValue ) const;
void SetParameter( int aParameterNumber, float f0, float f1, float f2, float f3 ) const;
void SetParameter( int aParameterNumber, const float* aMatrix4x4 ) const; ///< Set 4x4 matrix
/**
* Get an attribute location.
*
* @param aAttributeName is the name of the attribute.
* @return the location.
*/
int GetAttribute( const std::string& aAttributeName ) const;
/**
* Read the shader source file
*
* @param aShaderSourceName is the shader source file name.
* @return the source as string
*/
static std::string ReadSource( const std::string& aShaderSourceName );
private:
/**
* Compile vertex of fragment shader source code into the program.
*/
bool loadShaderFromStringArray( SHADER_TYPE aShaderType, const char** aArray, size_t aSize );
/**
* Get the shader program information.
*
* @param aProgram is the program number.
*/
void programInfo( GLuint aProgram );
/**
* Get the shader information.
*
* @param aShader is the shader number.
*/
void shaderInfo( GLuint aShader );
std::deque<GLuint> shaderNumbers; ///< Shader number list
GLuint programNumber; ///< Shader program number
bool isProgramCreated; ///< Flag for program creation
bool isShaderLinked; ///< Is the shader linked?
bool active; ///< Is any of shaders used?
GLuint maximumVertices; ///< The maximum of vertices to be generated
///< Input type [e.g. GL_LINES, GL_TRIANGLES, GL_QUADS etc.]
GLuint geomInputType;
///< Output type [e.g. GL_LINES, GL_TRIANGLES, GL_QUADS etc.]
GLuint geomOutputType;
std::deque<GLint> parameterLocation; ///< Location of the parameter
};
} // namespace KIGFX
#endif /* SHADER_H_ */

View file

@ -1,199 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2016-2017 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <confirm.h> // DisplayError
#include "kiglew.h" // Must be included first
#include <stdexcept>
#include <wx/log.h> // wxLogDebug
/**
* Flag to enable debug output of the GAL OpenGL error checking.
*
* Use "KICAD_GAL_OPENGL_ERROR" to enable GAL OpenGL error tracing.
*
* @ingroup trace_env_vars
*/
static const wxChar* const traceGalOpenGlError = wxT( "KICAD_GAL_OPENGL_ERROR" );
int checkGlError( const std::string& aInfo, const char* aFile, int aLine, bool aThrow )
{
int result = glGetError();
wxString errorMsg;
switch( result )
{
case GL_NO_ERROR:
// all good
break;
case GL_INVALID_ENUM:
errorMsg = wxString::Format( "Error: %s: invalid enum", aInfo );
break;
case GL_INVALID_VALUE:
errorMsg = wxString::Format( "Error: %s: invalid value", aInfo );
break;
case GL_INVALID_OPERATION:
errorMsg = wxString::Format( "Error: %s: invalid operation", aInfo );
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
{
GLenum status = glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT );
if( status != GL_FRAMEBUFFER_COMPLETE_EXT )
{
switch( status )
{
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
errorMsg = "The framebuffer attachment points are incomplete.";
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
errorMsg = "No images attached to the framebuffer.";
break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
errorMsg = "The framebuffer does not have at least one image attached to it.";
break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
errorMsg = "The framebuffer read buffer is incomplete.";
break;
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
errorMsg = "The combination of internal formats of the attached images violates "
"an implementation dependent set of restrictions.";
break;
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
errorMsg = "GL_RENDERBUFFER_SAMPLES is not the same for all attached render "
"buffers.";
break;
case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT:
errorMsg = "Framebuffer incomplete layer targets errors.";
break;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
errorMsg = "Framebuffer attachments have different dimensions";
break;
default:
errorMsg.Printf( "Unknown incomplete framebuffer error id %X", status );
}
}
else
{
errorMsg = wxString::Format( "Error: %s: invalid framebuffer operation", aInfo );
}
}
break;
case GL_OUT_OF_MEMORY:
errorMsg = wxString::Format( "Error: %s: out of memory", aInfo );
break;
case GL_STACK_UNDERFLOW:
errorMsg = wxString::Format( "Error: %s: stack underflow", aInfo );
break;
case GL_STACK_OVERFLOW:
errorMsg = wxString::Format( "Error: %s: stack overflow", aInfo );
break;
default:
errorMsg = wxString::Format( "Error: %s: unknown error", aInfo );
break;
}
if( result != GL_NO_ERROR )
{
if( aThrow )
{
wxLogTrace( traceGalOpenGlError, wxT( "Throwing exception for glGetError() '%s' "
"in file '%s' on line %d." ),
errorMsg,
aFile,
aLine );
throw std::runtime_error( (const char*) errorMsg.char_str() );
}
else
{
wxString msg = wxString::Format( wxT( "glGetError() '%s' in file '%s' on line %d." ),
errorMsg,
aFile,
aLine );
DisplayErrorMessage( nullptr, "OpenGL Error", errorMsg );
}
}
return result;
}
// debugMsgCallback is a callback function for glDebugMessageCallback.
// It must have the right type ( GLAPIENTRY )
static void GLAPIENTRY debugMsgCallback( GLenum aSource, GLenum aType, GLuint aId, GLenum aSeverity,
GLsizei aLength, const GLchar* aMessage,
const void* aUserParam )
{
switch( aSeverity )
{
case GL_DEBUG_SEVERITY_HIGH:
wxLogTrace( traceGalOpenGlError, wxS( "OpenGL ERROR: %s" ), aMessage );
break;
case GL_DEBUG_SEVERITY_MEDIUM:
wxLogTrace( traceGalOpenGlError, wxS( "OpenGL WARNING: %s" ), aMessage );
break;
case GL_DEBUG_SEVERITY_LOW:
wxLogTrace( traceGalOpenGlError, wxS( "OpenGL INFO: %s" ), aMessage );
break;
case GL_DEBUG_SEVERITY_NOTIFICATION:
return;
}
}
void enableGlDebug( bool aEnable )
{
if( aEnable )
{
glEnable( GL_DEBUG_OUTPUT );
glDebugMessageCallback( (GLDEBUGPROC) debugMsgCallback, nullptr );
}
else
{
glDisable( GL_DEBUG_OUTPUT );
}
}

View file

@ -1,51 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2016-2017 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef __OPENGL_UTILS_H
#define __OPENGL_UTILS_H
#include <string>
/**
* Check if a recent OpenGL operation has failed. If so, display the appropriate message
* starting with \a aInfo string to give more details.
*
* @param aInfo is the beginning of the error message.
* @param aFile is the file where the error occurred defined by the C __FILE__ variable.
* @param aLine is the line in \a aFile where the error occurred defined by the C __LINE__
* variable.
* @param aThrow an exception is thrown when true, otherwise only an error message is displayed.
* @return GL_NO_ERROR in case of no errors or one of GL_ constants returned by glGetError().
*/
int checkGlError( const std::string& aInfo, const char* aFile, int aLine, bool aThrow = true );
/**
* Enable or disable OpenGL driver messages output.
*
* @param aEnable decides whether the message should be shown.
*/
void enableGlDebug( bool aEnable );
#endif /* __OPENGL_ERROR_H */

View file

@ -1,90 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file vertex_common.h
* @brief Common defines and consts used in vertex related classes.
*/
#ifndef VERTEX_COMMON_H_
#define VERTEX_COMMON_H_
#include "kiglew.h" // Must be included first
#include <math/vector2d.h>
#include <cstddef>
namespace KIGFX
{
///< Possible types of shaders (keep consistent with the actual shader source in
///< kicad_vert.glsl and kicad_frag.glsl).
enum SHADER_MODE
{
SHADER_NONE = 0,
SHADER_FILLED_CIRCLE = 2,
SHADER_STROKED_CIRCLE = 3,
SHADER_FONT = 4,
SHADER_LINE_A = 5,
SHADER_LINE_B = 6,
SHADER_LINE_C = 7,
SHADER_LINE_D = 8,
SHADER_LINE_E = 9,
SHADER_LINE_F = 10,
SHADER_HOLE_WALL = 11
};
///< Data structure for vertices {X,Y,Z,R,G,B,A,shader&param}
struct VERTEX
{
GLfloat x, y, z; // Coordinates
GLubyte r, g, b, a; // Color
GLfloat shader[4]; // Shader type & params
};
static constexpr size_t VERTEX_SIZE = sizeof( VERTEX );
static constexpr size_t VERTEX_STRIDE = VERTEX_SIZE / sizeof( GLfloat );
static constexpr size_t COORD_OFFSET = offsetof( VERTEX, x );
static constexpr size_t COORD_SIZE = sizeof( VERTEX::x ) + sizeof( VERTEX::y ) +
sizeof( VERTEX::z );
static constexpr size_t COORD_STRIDE = COORD_SIZE / sizeof( GLfloat );
static constexpr size_t COLOR_OFFSET = offsetof( VERTEX, r );
static constexpr size_t COLOR_SIZE = sizeof( VERTEX::r ) + sizeof( VERTEX::g ) +
sizeof( VERTEX::b ) + sizeof( VERTEX::a );
static constexpr size_t COLOR_STRIDE = COLOR_SIZE / sizeof( GLubyte );
// Shader attributes
static constexpr size_t SHADER_OFFSET = offsetof( VERTEX, shader );
static constexpr size_t SHADER_SIZE = sizeof( VERTEX::shader );
static constexpr size_t SHADER_STRIDE = SHADER_SIZE / sizeof( GLfloat );
static constexpr size_t INDEX_SIZE = sizeof( GLuint );
} // namespace KIGFX
#endif /* VERTEX_COMMON_H_ */

View file

@ -1,73 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file vertex_container.cpp
* @brief Class to store vertices and handle transfers between system memory and GPU memory.
*/
#include "vertex_container.h"
#include "cached_container_ram.h"
#include "cached_container_gpu.h"
#include "noncached_container.h"
#include "shader.h"
#include <cstring>
using namespace KIGFX;
VERTEX_CONTAINER* VERTEX_CONTAINER::MakeContainer( bool aCached )
{
if( aCached )
{
const char* vendor = (const char*) glGetString( GL_VENDOR );
// Open source drivers do not cope well with GPU memory mapping,
// so the vertex data has to be kept in RAM
if( strstr( vendor, "X.Org" ) || strstr( vendor, "nouveau" ) )
return new CACHED_CONTAINER_RAM;
else
return new CACHED_CONTAINER_GPU;
}
return new NONCACHED_CONTAINER;
}
VERTEX_CONTAINER::VERTEX_CONTAINER( unsigned int aSize ) :
m_freeSpace( aSize ),
m_currentSize( aSize ),
m_initialSize( aSize ),
m_vertices( nullptr ),
m_failed( false ),
m_dirty( true )
{
}
VERTEX_CONTAINER::~VERTEX_CONTAINER()
{
}

View file

@ -1,191 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright 2013-2017 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file vertex_container.h
* Class to store vertices and handle transfers between system memory and GPU memory.
*/
#ifndef VERTEX_CONTAINER_H_
#define VERTEX_CONTAINER_H_
#include "vertex_common.h"
namespace KIGFX
{
class VERTEX_ITEM;
class SHADER;
class VERTEX_CONTAINER
{
public:
/**
* Return a pointer to a new container of an appropriate type.
*/
static VERTEX_CONTAINER* MakeContainer( bool aCached );
virtual ~VERTEX_CONTAINER();
/**
* Return true if the container caches vertex data in RAM or video memory.
* Otherwise it is a single batch draw which is later discarded.
*/
virtual bool IsCached() const = 0;
/**
* Prepare the container for vertices updates.
*/
virtual void Map() {}
/**
* Finish the vertices updates stage.
*/
virtual void Unmap() {}
/**
* Set the item for the further actions.
*
* @param aItem is the item or NULL in case of finishing the item.
*/
virtual void SetItem( VERTEX_ITEM* aItem ) = 0;
/**
* Clean up after adding an item.
*/
virtual void FinishItem() {};
/**
* Return allocated space for the requested number of vertices associated with the
* current item (set with SetItem()).
*
* The allocated space is added at the end of the chunk used by the current item and
* may serve to store new vertices.
*
* @param aSize is the number of vertices to be allocated.
* @return Pointer to the allocated space or NULL in case of failure.
*/
virtual VERTEX* Allocate( unsigned int aSize ) = 0;
/**
* Erase the data related to an item.
*
* @param aItem is the item to be erased.
*/
virtual void Delete( VERTEX_ITEM* aItem ) = 0;
/**
* Remove all data stored in the container and restores its original state.
*/
virtual void Clear() = 0;
/**
* Return pointer to the vertices stored in the container.
*/
VERTEX* GetAllVertices() const
{
return m_vertices;
}
/**
* Return vertices stored at the specific offset.
*
* @param aOffset is the offset.
*/
virtual VERTEX* GetVertices( unsigned int aOffset ) const
{
return &m_vertices[aOffset];
}
/**
* Return amount of vertices currently stored in the container.
*/
virtual unsigned int GetSize() const
{
return m_currentSize;
}
/**
* Return information about the container cache state.
*
* @return True in case the vertices have to be reuploaded.
*/
bool IsDirty() const
{
return m_dirty;
}
/**
* Set the dirty flag, so vertices in the container are going to be reuploaded to the GPU on
* the next frame.
*/
void SetDirty()
{
m_dirty = true;
}
/**
* Clear the dirty flag to prevent reuploading vertices to the GPU memory.
*/
void ClearDirty()
{
m_dirty = false;
}
protected:
VERTEX_CONTAINER( unsigned int aSize = DEFAULT_SIZE );
/**
* Return size of the used memory space.
*
* @return Size of the used memory space (expressed as a number of vertices).
*/
unsigned int usedSpace() const
{
return m_currentSize - m_freeSpace;
}
///< Free space left in the container, expressed in vertices
unsigned int m_freeSpace;
///< Current container size, expressed in vertices
unsigned int m_currentSize;
///< Store the initial size, so it can be resized to this on Clear()
unsigned int m_initialSize;
///< Actual storage memory
VERTEX* m_vertices;
// Status flags
bool m_failed;
bool m_dirty;
///< Default initial size of a container (expressed in vertices)
static constexpr unsigned int DEFAULT_SIZE = 1048576;
};
} // namespace KIGFX
#endif /* VERTEX_CONTAINER_H_ */

View file

@ -1,57 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file vertex_item.cpp
* @brief Class to handle an item held in a container.
*/
#include "vertex_item.h"
#include "vertex_manager.h"
#include <cstring>
using namespace KIGFX;
VERTEX_ITEM::VERTEX_ITEM( const VERTEX_MANAGER& aManager ) :
m_manager( aManager ),
m_offset( 0 ),
m_size( 0 )
{
// As the item is created, we are going to modify it, so call to SetItem() is needed
m_manager.SetItem( *this );
}
VERTEX_ITEM::~VERTEX_ITEM()
{
m_manager.FreeItem( *this );
}
VERTEX* VERTEX_ITEM::GetVertices() const
{
return m_manager.GetVertices( *this );
}

View file

@ -1,105 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file vertex_item.h
* Class to handle an item held in a container.
*/
#ifndef VERTEX_ITEM_H_
#define VERTEX_ITEM_H_
#include "vertex_common.h"
#include <gal/color4d.h>
#include <cstddef>
namespace KIGFX
{
class VERTEX_MANAGER;
class VERTEX_ITEM
{
public:
friend class CACHED_CONTAINER;
friend class CACHED_CONTAINER_GPU;
friend class VERTEX_MANAGER;
explicit VERTEX_ITEM( const VERTEX_MANAGER& aManager );
~VERTEX_ITEM();
/**
* Return information about number of vertices stored.
*
* @return Number of vertices.
*/
inline unsigned int GetSize() const
{
return m_size;
}
/**
* Return data offset in the container.
*
* @return Data offset expressed as a number of vertices.
*/
inline unsigned int GetOffset() const
{
return m_offset;
}
/**
* Return pointer to the data used by the VERTEX_ITEM.
*/
VERTEX* GetVertices() const;
private:
/**
* Set data offset in the container.
*
* @param aOffset is the offset expressed as a number of vertices.
*/
inline void setOffset( unsigned int aOffset )
{
m_offset = aOffset;
}
/**
* Set data size in the container.
*
* @param aSize is the size expressed as a number of vertices.
*/
inline void setSize( unsigned int aSize )
{
m_size = aSize;
}
const VERTEX_MANAGER& m_manager;
unsigned int m_offset;
unsigned int m_size;
};
} // namespace KIGFX
#endif /* VERTEX_ITEM_H_ */

View file

@ -1,318 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013-2016 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file vertex_manager.cpp
* @brief Class to control vertex container and GPU with possibility of emulating old-style OpenGL
* 1.0 state machine using modern OpenGL methods.
*/
#include "vertex_manager.h"
#include "cached_container.h"
#include "noncached_container.h"
#include "gpu_manager.h"
#include "vertex_item.h"
#include <confirm.h>
#include <wx/log.h>
/**
* Flag to enable #VERTEX_MANAGER debugging output.
*
* @ingroup trace_env_vars
*/
static const wxChar traceVertexManager[] = wxT( "KICAD_VERTEX_MANAGER" );
using namespace KIGFX;
VERTEX_MANAGER::VERTEX_MANAGER( bool aCached ) :
m_noTransform( true ),
m_transform( 1.0f ),
m_reserved( nullptr ),
m_reservedSpace( 0 )
{
m_container.reset( VERTEX_CONTAINER::MakeContainer( aCached ) );
m_gpu.reset( GPU_MANAGER::MakeManager( m_container.get() ) );
// There is no shader used by default
for( unsigned int i = 0; i < SHADER_STRIDE; ++i )
m_shader[i] = 0.0f;
}
void VERTEX_MANAGER::Map()
{
m_container->Map();
}
void VERTEX_MANAGER::Unmap()
{
m_container->Unmap();
}
bool VERTEX_MANAGER::Reserve( unsigned int aSize )
{
if( !aSize )
return true;
// flags to avoid hanging by calling DisplayError too many times:
static bool show_err_reserve = true;
static bool show_err_alloc = true;
if( m_reservedSpace != 0 || m_reserved )
{
if( show_err_reserve )
{
DisplayError( nullptr, wxT( "VERTEX_MANAGER::Reserve: Did not use all previous vertices allocated" ) );
show_err_reserve = false;
}
}
m_reserved = m_container->Allocate( aSize );
if( m_reserved == nullptr )
{
if( show_err_alloc )
{
DisplayError( nullptr, wxT( "VERTEX_MANAGER::Reserve: Vertex allocation error" ) );
show_err_alloc = false;
}
return false;
}
m_reservedSpace = aSize;
return true;
}
bool VERTEX_MANAGER::Vertex( GLfloat aX, GLfloat aY, GLfloat aZ )
{
// flag to avoid hanging by calling DisplayError too many times:
static bool show_err = true;
// Obtain the pointer to the vertex in the currently used container
VERTEX* newVertex;
if( m_reservedSpace > 0 )
{
newVertex = m_reserved++;
--m_reservedSpace;
if( m_reservedSpace == 0 )
m_reserved = nullptr;
}
else
{
newVertex = m_container->Allocate( 1 );
if( newVertex == nullptr )
{
if( show_err )
{
DisplayError( nullptr, wxT( "VERTEX_MANAGER::Vertex: Vertex allocation error" ) );
show_err = false;
}
return false;
}
}
putVertex( *newVertex, aX, aY, aZ );
return true;
}
bool VERTEX_MANAGER::Vertices( const VERTEX aVertices[], unsigned int aSize )
{
// flag to avoid hanging by calling DisplayError too many times:
static bool show_err = true;
// Obtain pointer to the vertex in currently used container
VERTEX* newVertex = m_container->Allocate( aSize );
if( newVertex == nullptr )
{
if( show_err )
{
DisplayError( nullptr, wxT( "VERTEX_MANAGER::Vertices: Vertex allocation error" ) );
show_err = false;
}
return false;
}
// Put vertices in already allocated memory chunk
for( unsigned int i = 0; i < aSize; ++i )
{
putVertex( newVertex[i], aVertices[i].x, aVertices[i].y, aVertices[i].z );
}
return true;
}
void VERTEX_MANAGER::SetItem( VERTEX_ITEM& aItem ) const
{
m_container->SetItem( &aItem );
}
void VERTEX_MANAGER::FinishItem() const
{
if( m_reservedSpace != 0 || m_reserved )
wxLogTrace( traceVertexManager, wxS( "Did not use all previous vertices allocated" ) );
m_container->FinishItem();
}
void VERTEX_MANAGER::FreeItem( VERTEX_ITEM& aItem ) const
{
m_container->Delete( &aItem );
}
void VERTEX_MANAGER::ChangeItemColor( const VERTEX_ITEM& aItem, const COLOR4D& aColor ) const
{
unsigned int size = aItem.GetSize();
unsigned int offset = aItem.GetOffset();
VERTEX* vertex = m_container->GetVertices( offset );
for( unsigned int i = 0; i < size; ++i )
{
vertex->r = aColor.r * 255.0;
vertex->g = aColor.g * 255.0;
vertex->b = aColor.b * 255.0;
vertex->a = aColor.a * 255.0;
vertex++;
}
m_container->SetDirty();
}
void VERTEX_MANAGER::ChangeItemDepth( const VERTEX_ITEM& aItem, GLfloat aDepth ) const
{
unsigned int size = aItem.GetSize();
unsigned int offset = aItem.GetOffset();
VERTEX* vertex = m_container->GetVertices( offset );
for( unsigned int i = 0; i < size; ++i )
{
vertex->z = aDepth;
vertex++;
}
m_container->SetDirty();
}
VERTEX* VERTEX_MANAGER::GetVertices( const VERTEX_ITEM& aItem ) const
{
if( aItem.GetSize() == 0 )
return nullptr; // The item is not stored in the container
return m_container->GetVertices( aItem.GetOffset() );
}
void VERTEX_MANAGER::SetShader( SHADER& aShader ) const
{
m_gpu->SetShader( aShader );
}
void VERTEX_MANAGER::Clear() const
{
m_container->Clear();
}
void VERTEX_MANAGER::BeginDrawing() const
{
m_gpu->BeginDrawing();
}
void VERTEX_MANAGER::DrawItem( const VERTEX_ITEM& aItem ) const
{
m_gpu->DrawIndices( &aItem );
}
void VERTEX_MANAGER::EndDrawing() const
{
m_gpu->EndDrawing();
}
void VERTEX_MANAGER::putVertex( VERTEX& aTarget, GLfloat aX, GLfloat aY, GLfloat aZ ) const
{
// Modify the vertex according to the currently used transformations
if( m_noTransform )
{
// Simply copy coordinates, when the transform matrix is the identity matrix
aTarget.x = aX;
aTarget.y = aY;
aTarget.z = aZ;
}
else
{
// Apply transformations
glm::vec4 transVertex( aX, aY, aZ, 1.0f );
transVertex = m_transform * transVertex;
aTarget.x = transVertex.x;
aTarget.y = transVertex.y;
aTarget.z = transVertex.z;
}
// Apply currently used color
aTarget.r = m_color[0];
aTarget.g = m_color[1];
aTarget.b = m_color[2];
aTarget.a = m_color[3];
// Apply currently used shader
for( unsigned int j = 0; j < SHADER_STRIDE; ++j )
{
aTarget.shader[j] = m_shader[j];
}
}
void VERTEX_MANAGER::EnableDepthTest( bool aEnabled )
{
m_gpu->EnableDepthTest( aEnabled );
}

View file

@ -1,405 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013-2016 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file vertex_manager.h
*/
#ifndef VERTEX_MANAGER_H_
#define VERTEX_MANAGER_H_
#define GLM_FORCE_RADIANS
#include <glm/gtc/matrix_transform.hpp>
#include <glm/glm.hpp>
#include "vertex_common.h"
#include <gal/color4d.h>
#include <stack>
#include <memory>
namespace KIGFX
{
class SHADER;
class VERTEX_ITEM;
class VERTEX_CONTAINER;
class GPU_MANAGER;
/**
* Class to control vertex container and GPU with possibility of emulating old-style OpenGL
* 1.0 state machine using modern OpenGL methods.
*/
class VERTEX_MANAGER
{
public:
/**
* @param aCached says if vertices should be cached in GPU or system memory. For data that
* does not change every frame, it is better to store vertices in GPU memory.
*/
VERTEX_MANAGER( bool aCached );
/**
* Map vertex buffer.
*/
void Map();
/**
* Unmap vertex buffer.
*/
void Unmap();
/**
* Allocate space for vertices, so it will be used with subsequent Vertex() calls.
*
* @param aSize is the number of vertices that should be available in the reserved space.
* @return True if successful, false otherwise.
*/
bool Reserve( unsigned int aSize );
/**
* Add a vertex with the given coordinates to the currently set item.
*
* Color & shader parameters stored in aVertex are ignored, instead color & shader set
* by Color() and Shader() functions are used. Vertex coordinates will have the current
* transformation matrix applied.
*
* @param aVertex contains vertex coordinates.
* @return True if successful, false otherwise.
*/
inline bool Vertex( const VERTEX& aVertex )
{
return Vertex( aVertex.x, aVertex.y, aVertex.z );
}
/**
* Add a vertex with the given coordinates to the currently set item.
*
* Vertex coordinates will have the current transformation matrix applied.
*
* @param aX is the X coordinate of the new vertex.
* @param aY is the Y coordinate of the new vertex.
* @param aZ is the Z coordinate of the new vertex.
* @return True if successful, false otherwise.
*/
bool Vertex( GLfloat aX, GLfloat aY, GLfloat aZ );
/**
* Add a vertex with the given coordinates to the currently set item.
*
* Vertex coordinates will have the current transformation matrix applied.
*
* @param aXY are the XY coordinates of the new vertex.
* @param aZ is the Z coordinate of the new vertex.
* @return True if successful, false otherwise.
*/
bool Vertex( const VECTOR2D& aXY, GLfloat aZ )
{
return Vertex( aXY.x, aXY.y, aZ );
}
/**
* Add one or more vertices to the currently set item.
*
* It takes advantage of allocating memory in advance, so should be faster than
* adding vertices one by one. Color & shader parameters stored in aVertices are
* ignored, instead color & shader set by Color() and Shader() functions are used.
* All the vertex coordinates will have the current transformation matrix applied.
*
* @param aVertices contains vertices to be added.
* @param aSize is the number of vertices to be added.
* @return True if successful, false otherwise.
*/
bool Vertices( const VERTEX aVertices[], unsigned int aSize );
/**
* Change currently used color that will be applied to newly added vertices.
*
* @param aColor is the new color.
*/
inline void Color( const COLOR4D& aColor )
{
m_color[0] = aColor.r * 255.0;
m_color[1] = aColor.g * 255.0;
m_color[2] = aColor.b * 255.0;
m_color[3] = aColor.a * 255.0;
}
/**
* Change currently used color that will be applied to newly added vertices.
*
* It is the equivalent of glColor4f() function.
*
* @param aRed is the red component of the new color.
* @param aGreen is the green component of the new color.
* @param aBlue is the blue component of the new color.
* @param aAlpha is the alpha component of the new color.
*/
inline void Color( GLfloat aRed, GLfloat aGreen, GLfloat aBlue, GLfloat aAlpha )
{
m_color[0] = aRed * 255.0;
m_color[1] = aGreen * 255.0;
m_color[2] = aBlue * 255.0;
m_color[3] = aAlpha * 255.0;
}
/**
* Change currently used shader and its parameters that will be applied to newly added
* vertices.
*
* Parameters depend on shader, for more information have a look at shaders source code.
*
* @see SHADER_TYPE
*
* @param aShaderType is the a shader type to be applied.
* @param aParam1 is the optional parameter for a shader.
* @param aParam2 is the optional parameter for a shader.
* @param aParam3 is the optional parameter for a shader.
*/
inline void Shader( GLfloat aShaderType, GLfloat aParam1 = 0.0f, GLfloat aParam2 = 0.0f,
GLfloat aParam3 = 0.0f )
{
m_shader[0] = aShaderType;
m_shader[1] = aParam1;
m_shader[2] = aParam2;
m_shader[3] = aParam3;
}
/**
* Multiply the current matrix by a translation matrix, so newly vertices will be
* translated by the given vector.
*
* It is the equivalent of the glTranslatef() function.
*
* @param aX is the X coordinate of a translation vector.
* @param aY is the X coordinate of a translation vector.
* @param aZ is the X coordinate of a translation vector.
*/
inline void Translate( GLfloat aX, GLfloat aY, GLfloat aZ )
{
m_transform = glm::translate( m_transform, glm::vec3( aX, aY, aZ ) );
}
/**
* Multiply the current matrix by a rotation matrix, so the newly vertices will be
* rotated by the given angles.
*
* It is the equivalent of the glRotatef() function.
*
* @param aAngle is the angle of rotation, in radians.
* @param aX is a multiplier for the X axis
* @param aY is a multiplier for the Y axis
* @param aZ is a multiplier for the Z axis.
*/
inline void Rotate( GLfloat aAngle, GLfloat aX, GLfloat aY, GLfloat aZ )
{
m_transform = glm::rotate( m_transform, aAngle, glm::vec3( aX, aY, aZ ) );
}
/**
* Multiply the current matrix by a scaling matrix, so the newly vertices will be
* scaled by the given factors.
*
* It is the equivalent of the glScalef() function.
*
* @param aX is the X axis scaling factor.
* @param aY is the Y axis scaling factor.
* @param aZ is the Z axis scaling factor.
*/
inline void Scale( GLfloat aX, GLfloat aY, GLfloat aZ )
{
m_transform = glm::scale( m_transform, glm::vec3( aX, aY, aZ ) );
}
/**
* Multiply the current transformation matrix by the given matrix.
*
* It is the equivalent of the glMultMatrixf() function.
*
* @param aMatrix is a 4x4 transformation matrix to multiply.
*/
inline void MultiplyMatrix( const glm::mat4& aMatrix )
{
m_transform = m_transform * aMatrix;
}
/**
* Push the current transformation matrix stack.
*
* It is the equivalent of the glPushMatrix() function.
*/
inline void PushMatrix()
{
m_transformStack.push( m_transform );
// Every transformation starts with PushMatrix
m_noTransform = false;
}
/**
* Pop the current transformation matrix stack.
*
* It is the equivalent of the glPopMatrix() function.
*/
void PopMatrix()
{
wxASSERT( !m_transformStack.empty() );
m_transform = m_transformStack.top();
m_transformStack.pop();
if( m_transformStack.empty() )
{
// We return back to the identity matrix, thus no vertex transformation is needed
m_noTransform = true;
}
}
/**
* Set an item to start its modifications.
*
* After calling the function it is possible to add vertices using function Add().
*
* @param aItem is the item that is going to store vertices in the container.
*/
void SetItem( VERTEX_ITEM& aItem ) const;
/**
* Clean after adding an item.
*/
void FinishItem() const;
/**
* Free the memory occupied by the item, so it is no longer stored in the container.
*
* @param aItem is the item to be freed
*/
void FreeItem( VERTEX_ITEM& aItem ) const;
/**
* Change the color of all vertices owned by an item.
*
* @param aItem is the item to change.
* @param aColor is the new color to be applied.
*/
void ChangeItemColor( const VERTEX_ITEM& aItem, const COLOR4D& aColor ) const;
/**
* Change the depth of all vertices owned by an item.
*
* @param aItem is the item to change.
* @param aDepth is the new color to be applied.
*/
void ChangeItemDepth( const VERTEX_ITEM& aItem, GLfloat aDepth ) const;
/**
* Return a pointer to the vertices owned by an item.
*
* @param aItem is the owner of vertices that are going to be returned.
* @return Pointer to the vertices or NULL if the item is not stored at the container.
*/
VERTEX* GetVertices( const VERTEX_ITEM& aItem ) const;
const glm::mat4& GetTransformation() const
{
return m_transform;
}
/**
* Set a shader program that is going to be used during rendering.
*
* @param aShader is the object containing compiled and linked shader program.
*/
void SetShader( SHADER& aShader ) const;
/**
* Remove all the stored vertices from the container.
*/
void Clear() const;
/**
* Prepare buffers and items to start drawing.
*/
void BeginDrawing() const;
/**
* Draw an item to the buffer.
*
* @param aItem is the item to be drawn.
*/
void DrawItem( const VERTEX_ITEM& aItem ) const;
/**
* Finish drawing operations.
*/
void EndDrawing() const;
/**
* Enable/disable Z buffer depth test.
*/
void EnableDepthTest( bool aEnabled );
protected:
/**
* Apply all transformation to the given coordinates and store them at the specified target.
*
* @param aTarget is the place where the new vertex is going to be stored (it has to be
* allocated first).
* @param aX is the X coordinate of the new vertex.
* @param aY is the Y coordinate of the new vertex.
* @param aZ is the Z coordinate of the new vertex.
*/
void putVertex( VERTEX& aTarget, GLfloat aX, GLfloat aY, GLfloat aZ ) const;
/// Container for vertices, may be cached or noncached
std::shared_ptr<VERTEX_CONTAINER> m_container;
/// GPU manager for data transfers and drawing operations
std::shared_ptr<GPU_MANAGER> m_gpu;
/// State machine variables
/// True in case there is no need to transform vertices
bool m_noTransform;
/// Currently used transform matrix
glm::mat4 m_transform;
/// Stack of transformation matrices, used for Push/PopMatrix
std::stack<glm::mat4> m_transformStack;
/// Currently used color
GLubyte m_color[COLOR_STRIDE];
/// Currently used shader and its parameters
GLfloat m_shader[SHADER_STRIDE];
/// Currently reserved chunk to store vertices
VERTEX* m_reserved;
/// Currently available reserved space
unsigned int m_reservedSpace;
};
} // namespace KIGFX
#endif /* VERTEX_MANAGER_H_ */

View file

@ -1,528 +0,0 @@
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "webgl_antialiasing.h"
#include "webgl_compositor.h"
#include "fullscreen_quad.h"
#include "utils.h"
#include <gal/color4d.h>
#include <memory>
#include <tuple>
#include <glsl_smaa_base.h>
#include <glsl_smaa_pass_1_frag_color.h>
#include <glsl_smaa_pass_1_frag_luma.h>
#include <glsl_smaa_pass_1_vert.h>
#include <glsl_smaa_pass_2_frag.h>
#include <glsl_smaa_pass_2_vert.h>
#include <glsl_smaa_pass_3_frag.h>
#include <glsl_smaa_pass_3_vert.h>
#include "SmaaAreaTex.h"
#include "SmaaSearchTex.h"
using namespace KIGFX;
// =========================
// ANTIALIASING_NONE
// =========================
ANTIALIASING_NONE::ANTIALIASING_NONE( WEBGL_COMPOSITOR* aCompositor ) :
compositor( aCompositor )
{
}
bool ANTIALIASING_NONE::Init()
{
// Nothing to initialize
return true;
}
VECTOR2I ANTIALIASING_NONE::GetInternalBufferSize()
{
return compositor->GetScreenSize();
}
void ANTIALIASING_NONE::DrawBuffer( GLuint buffer )
{
compositor->DrawBuffer( buffer, WEBGL_COMPOSITOR::DIRECT_RENDERING );
}
void ANTIALIASING_NONE::Present()
{
// Nothing to present, draw_buffer already drew to the screen
}
void ANTIALIASING_NONE::OnLostBuffers()
{
// Nothing to do
}
void ANTIALIASING_NONE::Begin()
{
// Nothing to do
}
unsigned int ANTIALIASING_NONE::CreateBuffer()
{
return compositor->CreateBuffer( compositor->GetScreenSize() );
}
namespace
{
void draw_fullscreen_primitive()
{
// Use VBO-based fullscreen quad (replaces legacy immediate mode)
KIGFX::GetFullscreenQuad().Draw();
}
} // namespace
// =========================
// ANTIALIASING_SUPERSAMPLING
// =========================
ANTIALIASING_SUPERSAMPLING::ANTIALIASING_SUPERSAMPLING( WEBGL_COMPOSITOR* aCompositor ) :
compositor( aCompositor ),
ssaaMainBuffer( 0 ), areBuffersCreated( false ), areShadersCreated( false )
{
}
bool ANTIALIASING_SUPERSAMPLING::Init()
{
areShadersCreated = false;
if( !areBuffersCreated )
{
ssaaMainBuffer = compositor->CreateBuffer();
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
areBuffersCreated = true;
}
return true;
}
VECTOR2I ANTIALIASING_SUPERSAMPLING::GetInternalBufferSize()
{
return compositor->GetScreenSize() * 2;
}
void ANTIALIASING_SUPERSAMPLING::Begin()
{
compositor->SetBuffer( ssaaMainBuffer );
compositor->ClearBuffer( COLOR4D::BLACK );
}
void ANTIALIASING_SUPERSAMPLING::DrawBuffer( GLuint aBuffer )
{
compositor->DrawBuffer( aBuffer, ssaaMainBuffer );
}
void ANTIALIASING_SUPERSAMPLING::Present()
{
glDisable( GL_BLEND );
glDisable( GL_DEPTH_TEST );
glActiveTexture( GL_TEXTURE0 );
glBindTexture( GL_TEXTURE_2D, compositor->GetBufferTexture( ssaaMainBuffer ) );
compositor->SetBuffer( WEBGL_COMPOSITOR::DIRECT_RENDERING );
glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE );
draw_fullscreen_primitive();
glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
}
void ANTIALIASING_SUPERSAMPLING::OnLostBuffers()
{
areBuffersCreated = false;
}
unsigned int ANTIALIASING_SUPERSAMPLING::CreateBuffer()
{
return compositor->CreateBuffer( GetInternalBufferSize() );
}
// ===============================
// ANTIALIASING_SMAA
// ===============================
ANTIALIASING_SMAA::ANTIALIASING_SMAA( WEBGL_COMPOSITOR* aCompositor ) :
areBuffersInitialized( false ),
shadersLoaded( false ),
compositor( aCompositor )
{
smaaBaseBuffer = 0;
smaaEdgesBuffer = 0;
smaaBlendBuffer = 0;
smaaAreaTex = 0;
smaaSearchTex = 0;
pass_1_metrics = 0;
pass_2_metrics = 0;
pass_3_metrics = 0;
}
VECTOR2I ANTIALIASING_SMAA::GetInternalBufferSize()
{
return compositor->GetScreenSize();
}
void ANTIALIASING_SMAA::loadShaders()
{
// Load constant textures
// Note: GL_TEXTURE_2D enable not needed in WebGL 2.0
glActiveTexture( GL_TEXTURE0 );
glGenTextures( 1, &smaaAreaTex );
glBindTexture( GL_TEXTURE_2D, smaaAreaTex );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RG8, AREATEX_WIDTH, AREATEX_HEIGHT, 0, GL_RG,
GL_UNSIGNED_BYTE, areaTexBytes );
checkGlError( "loading smaa area tex", __FILE__, __LINE__ );
glGenTextures( 1, &smaaSearchTex );
glBindTexture( GL_TEXTURE_2D, smaaSearchTex );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexImage2D( GL_TEXTURE_2D, 0, GL_R8, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, 0, GL_RED,
GL_UNSIGNED_BYTE, searchTexBytes );
checkGlError( "loading smaa search tex", __FILE__, __LINE__ );
// Quality settings:
// THRESHOLD: intended to exclude spurious edges in photorealistic game graphics
// but in a high-contrast CAD application, all edges are intentional
// should be set fairly low, so user color choices do not affect antialiasing
// MAX_SEARCH_STEPS: steps of 2px, searched in H/V direction to discover true angle of edges
// improves AA for lines close H/V but creates fuzzyness at junctions
// MAX_SEARCH_STEPS_DIAG: steps of 1px, searched in diagonal direction
// improves lines close to 45deg but turns small circles into octagons
// CORNER_ROUNDING: SMAA can distinguish actual corners from aliasing jaggies,
// we want to preserve those as much as possible
// Edge Detection: In Eeschema, when a single pixel line changes color, edge detection using
// color is too aggressive and leads to a white spot at the transition point
std::string quality_string;
std::string edge_detect_shader;
// trades imperfect AA of shallow angles for a near artifact-free reproduction of fine features
// jaggies are smoothed over max 5px (original step + 2px in both directions)
quality_string = "#define SMAA_THRESHOLD 0.005\n"
"#define SMAA_MAX_SEARCH_STEPS 1\n"
"#define SMAA_MAX_SEARCH_STEPS_DIAG 2\n"
"#define SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR 1.5\n"
"#define SMAA_CORNER_ROUNDING 0\n";
edge_detect_shader = BUILTIN_SHADERS::glsl_smaa_pass_1_frag_luma;
// set up shaders - Use GLSL ES 3.00 for WebGL 2.0
std::string vert_preamble( R"SHADER(
#version 300 es
precision highp float;
precision highp int;
#define SMAA_GLSL_3
#define SMAA_INCLUDE_VS 1
#define SMAA_INCLUDE_PS 0
uniform vec4 SMAA_RT_METRICS;
in vec4 a_vertex;
in vec4 a_texCoord0;
)SHADER" );
std::string frag_preamble( R"SHADER(
#version 300 es
precision highp float;
precision highp int;
out vec4 fragColor;
#define SMAA_GLSL_3
#define SMAA_INCLUDE_VS 0
#define SMAA_INCLUDE_PS 1
uniform vec4 SMAA_RT_METRICS;
)SHADER" );
//
// Set up pass 1 Shader
//
pass_1_shader = std::make_unique<SHADER>();
pass_1_shader->LoadShaderFromStrings( KIGFX::SHADER_TYPE_VERTEX, vert_preamble, quality_string,
BUILTIN_SHADERS::glsl_smaa_base,
BUILTIN_SHADERS::glsl_smaa_pass_1_vert );
pass_1_shader->LoadShaderFromStrings( KIGFX::SHADER_TYPE_FRAGMENT, frag_preamble,
quality_string, BUILTIN_SHADERS::glsl_smaa_base,
edge_detect_shader );
pass_1_shader->Link();
checkGlError( "linking pass 1 shader", __FILE__, __LINE__ );
GLint smaaColorTexParameter = pass_1_shader->AddParameter( "colorTex" );
checkGlError( "pass1: getting colorTex uniform", __FILE__, __LINE__ );
pass_1_metrics = pass_1_shader->AddParameter( "SMAA_RT_METRICS" );
checkGlError( "pass1: getting metrics uniform", __FILE__, __LINE__ );
pass_1_shader->Use();
checkGlError( "pass1: using shader", __FILE__, __LINE__ );
pass_1_shader->SetParameter( smaaColorTexParameter, 0 );
checkGlError( "pass1: setting colorTex uniform", __FILE__, __LINE__ );
pass_1_shader->Deactivate();
checkGlError( "pass1: deactivating shader", __FILE__, __LINE__ );
//
// set up pass 2 shader
//
pass_2_shader = std::make_unique<SHADER>();
pass_2_shader->LoadShaderFromStrings( KIGFX::SHADER_TYPE_VERTEX, vert_preamble, quality_string,
BUILTIN_SHADERS::glsl_smaa_base,
BUILTIN_SHADERS::glsl_smaa_pass_2_vert );
pass_2_shader->LoadShaderFromStrings( KIGFX::SHADER_TYPE_FRAGMENT, frag_preamble,
quality_string, BUILTIN_SHADERS::glsl_smaa_base,
BUILTIN_SHADERS::glsl_smaa_pass_2_frag );
pass_2_shader->Link();
checkGlError( "linking pass 2 shader", __FILE__, __LINE__ );
GLint smaaEdgesTexParameter = pass_2_shader->AddParameter( "edgesTex" );
checkGlError( "pass2: getting colorTex uniform", __FILE__, __LINE__ );
GLint smaaAreaTexParameter = pass_2_shader->AddParameter( "areaTex" );
checkGlError( "pass2: getting areaTex uniform", __FILE__, __LINE__ );
GLint smaaSearchTexParameter = pass_2_shader->AddParameter( "searchTex" );
checkGlError( "pass2: getting searchTex uniform", __FILE__, __LINE__ );
pass_2_metrics = pass_2_shader->AddParameter( "SMAA_RT_METRICS" );
checkGlError( "pass2: getting metrics uniform", __FILE__, __LINE__ );
pass_2_shader->Use();
checkGlError( "pass2: using shader", __FILE__, __LINE__ );
pass_2_shader->SetParameter( smaaEdgesTexParameter, 0 );
checkGlError( "pass2: setting colorTex uniform", __FILE__, __LINE__ );
pass_2_shader->SetParameter( smaaAreaTexParameter, 1 );
checkGlError( "pass2: setting areaTex uniform", __FILE__, __LINE__ );
pass_2_shader->SetParameter( smaaSearchTexParameter, 3 );
checkGlError( "pass2: setting searchTex uniform", __FILE__, __LINE__ );
pass_2_shader->Deactivate();
checkGlError( "pass2: deactivating shader", __FILE__, __LINE__ );
//
// set up pass 3 shader
//
pass_3_shader = std::make_unique<SHADER>();
pass_3_shader->LoadShaderFromStrings( KIGFX::SHADER_TYPE_VERTEX, vert_preamble, quality_string,
BUILTIN_SHADERS::glsl_smaa_base,
BUILTIN_SHADERS::glsl_smaa_pass_3_vert );
pass_3_shader->LoadShaderFromStrings( KIGFX::SHADER_TYPE_FRAGMENT, frag_preamble,
quality_string, BUILTIN_SHADERS::glsl_smaa_base,
BUILTIN_SHADERS::glsl_smaa_pass_3_frag );
pass_3_shader->Link();
GLint smaaP3ColorTexParameter = pass_3_shader->AddParameter( "colorTex" );
checkGlError( "pass3: getting colorTex uniform", __FILE__, __LINE__ );
GLint smaaBlendTexParameter = pass_3_shader->AddParameter( "blendTex" );
checkGlError( "pass3: getting blendTex uniform", __FILE__, __LINE__ );
pass_3_metrics = pass_3_shader->AddParameter( "SMAA_RT_METRICS" );
checkGlError( "pass3: getting metrics uniform", __FILE__, __LINE__ );
pass_3_shader->Use();
checkGlError( "pass3: using shader", __FILE__, __LINE__ );
pass_3_shader->SetParameter( smaaP3ColorTexParameter, 0 );
checkGlError( "pass3: setting colorTex uniform", __FILE__, __LINE__ );
pass_3_shader->SetParameter( smaaBlendTexParameter, 1 );
checkGlError( "pass3: setting blendTex uniform", __FILE__, __LINE__ );
pass_3_shader->Deactivate();
checkGlError( "pass3: deactivating shader", __FILE__, __LINE__ );
shadersLoaded = true;
}
void ANTIALIASING_SMAA::updateUniforms()
{
auto dims = compositor->GetScreenSize();
pass_1_shader->Use();
checkGlError( "pass1: using shader", __FILE__, __LINE__ );
pass_1_shader->SetParameter( pass_1_metrics, 1.f / float( dims.x ), 1.f / float( dims.y ),
float( dims.x ), float( dims.y ) );
checkGlError( "pass1: setting metrics uniform", __FILE__, __LINE__ );
pass_1_shader->Deactivate();
checkGlError( "pass1: deactivating shader", __FILE__, __LINE__ );
pass_2_shader->Use();
checkGlError( "pass2: using shader", __FILE__, __LINE__ );
pass_2_shader->SetParameter( pass_2_metrics, 1.f / float( dims.x ), 1.f / float( dims.y ),
float( dims.x ), float( dims.y ) );
checkGlError( "pass2: setting metrics uniform", __FILE__, __LINE__ );
pass_2_shader->Deactivate();
checkGlError( "pass2: deactivating shader", __FILE__, __LINE__ );
pass_3_shader->Use();
checkGlError( "pass3: using shader", __FILE__, __LINE__ );
pass_3_shader->SetParameter( pass_3_metrics, 1.f / float( dims.x ), 1.f / float( dims.y ),
float( dims.x ), float( dims.y ) );
checkGlError( "pass3: setting metrics uniform", __FILE__, __LINE__ );
pass_3_shader->Deactivate();
checkGlError( "pass3: deactivating shader", __FILE__, __LINE__ );
}
bool ANTIALIASING_SMAA::Init()
{
if( !shadersLoaded )
loadShaders();
if( !areBuffersInitialized )
{
smaaBaseBuffer = compositor->CreateBuffer();
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
smaaEdgesBuffer = compositor->CreateBuffer();
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
smaaBlendBuffer = compositor->CreateBuffer();
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
updateUniforms();
areBuffersInitialized = true;
}
// Nothing to initialize
return true;
}
void ANTIALIASING_SMAA::OnLostBuffers()
{
areBuffersInitialized = false;
}
unsigned int ANTIALIASING_SMAA::CreateBuffer()
{
return compositor->CreateBuffer( compositor->GetScreenSize() );
}
void ANTIALIASING_SMAA::DrawBuffer( GLuint buffer )
{
// draw to internal buffer
compositor->DrawBuffer( buffer, smaaBaseBuffer );
}
void ANTIALIASING_SMAA::Begin()
{
compositor->SetBuffer( smaaBaseBuffer );
compositor->ClearBuffer( COLOR4D::BLACK );
}
namespace
{
void draw_fullscreen_triangle()
{
// Use VBO-based fullscreen triangle (replaces legacy immediate mode)
KIGFX::GetFullscreenQuad().DrawTriangle();
}
} // namespace
void ANTIALIASING_SMAA::Present()
{
auto sourceTexture = compositor->GetBufferTexture( smaaBaseBuffer );
glDisable( GL_BLEND );
glDisable( GL_DEPTH_TEST );
// Note: GL_TEXTURE_2D enable not needed in WebGL 2.0
//
// pass 1: main-buffer -> smaaEdgesBuffer
//
compositor->SetBuffer( smaaEdgesBuffer );
compositor->ClearBuffer( COLOR4D::BLACK );
glActiveTexture( GL_TEXTURE0 );
glBindTexture( GL_TEXTURE_2D, sourceTexture );
checkGlError( "binding colorTex", __FILE__, __LINE__ );
pass_1_shader->Use();
checkGlError( "using smaa pass 1 shader", __FILE__, __LINE__ );
draw_fullscreen_triangle();
pass_1_shader->Deactivate();
//
// pass 2: smaaEdgesBuffer -> smaaBlendBuffer
//
compositor->SetBuffer( smaaBlendBuffer );
compositor->ClearBuffer( COLOR4D::BLACK );
auto edgesTex = compositor->GetBufferTexture( smaaEdgesBuffer );
glActiveTexture( GL_TEXTURE0 );
glBindTexture( GL_TEXTURE_2D, edgesTex );
glActiveTexture( GL_TEXTURE1 );
glBindTexture( GL_TEXTURE_2D, smaaAreaTex );
glActiveTexture( GL_TEXTURE3 );
glBindTexture( GL_TEXTURE_2D, smaaSearchTex );
pass_2_shader->Use();
draw_fullscreen_triangle();
pass_2_shader->Deactivate();
//
// pass 3: colorTex + BlendBuffer -> output
//
compositor->SetBuffer( WEBGL_COMPOSITOR::DIRECT_RENDERING );
compositor->ClearBuffer( COLOR4D::BLACK );
auto blendTex = compositor->GetBufferTexture( smaaBlendBuffer );
glActiveTexture( GL_TEXTURE0 );
glBindTexture( GL_TEXTURE_2D, sourceTexture );
glActiveTexture( GL_TEXTURE1 );
glBindTexture( GL_TEXTURE_2D, blendTex );
glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE );
pass_3_shader->Use();
draw_fullscreen_triangle();
pass_3_shader->Deactivate();
glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
}

View file

@ -1,143 +0,0 @@
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef OPENGL_ANTIALIASING_H__
#define OPENGL_ANTIALIASING_H__
#include <memory>
#include "shader.h"
#include <math/vector2d.h>
namespace KIGFX {
class WEBGL_COMPOSITOR;
class OPENGL_PRESENTOR
{
public:
virtual ~OPENGL_PRESENTOR()
{
}
virtual bool Init() = 0;
virtual unsigned int CreateBuffer() = 0;
virtual VECTOR2I GetInternalBufferSize() = 0;
virtual void OnLostBuffers() = 0;
virtual void Begin() = 0;
virtual void DrawBuffer( GLuint aBuffer ) = 0;
virtual void Present() = 0;
};
class ANTIALIASING_NONE : public OPENGL_PRESENTOR
{
public:
ANTIALIASING_NONE( WEBGL_COMPOSITOR* aCompositor );
bool Init() override;
unsigned int CreateBuffer() override;
VECTOR2I GetInternalBufferSize() override;
void OnLostBuffers() override;
void Begin() override;
void DrawBuffer( GLuint aBuffer ) override;
void Present() override;
private:
WEBGL_COMPOSITOR* compositor;
};
class ANTIALIASING_SUPERSAMPLING : public OPENGL_PRESENTOR
{
public:
ANTIALIASING_SUPERSAMPLING( WEBGL_COMPOSITOR* aCompositor );
bool Init() override;
unsigned int CreateBuffer() override;
VECTOR2I GetInternalBufferSize() override;
void OnLostBuffers() override;
void Begin() override;
void DrawBuffer( GLuint ) override;
void Present() override;
private:
WEBGL_COMPOSITOR* compositor;
unsigned int ssaaMainBuffer;
bool areBuffersCreated;
bool areShadersCreated;
};
class ANTIALIASING_SMAA : public OPENGL_PRESENTOR
{
public:
ANTIALIASING_SMAA( WEBGL_COMPOSITOR* aCompositor );
bool Init() override;
unsigned int CreateBuffer () override;
VECTOR2I GetInternalBufferSize() override;
void OnLostBuffers() override;
void Begin() override;
void DrawBuffer( GLuint buffer ) override;
void Present() override;
private:
void loadShaders();
void updateUniforms();
bool areBuffersInitialized;
unsigned int smaaBaseBuffer; // base + overlay temporary
unsigned int smaaEdgesBuffer;
unsigned int smaaBlendBuffer;
// smaa shader lookup textures
unsigned int smaaAreaTex;
unsigned int smaaSearchTex;
bool shadersLoaded;
std::unique_ptr<SHADER> pass_1_shader;
GLint pass_1_metrics;
std::unique_ptr<SHADER> pass_2_shader;
GLint pass_2_metrics;
std::unique_ptr<SHADER> pass_3_shader;
GLint pass_3_metrics;
WEBGL_COMPOSITOR* compositor;
};
}
#endif

View file

@ -1,470 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013-2017 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file opengl_compositor.cpp
* @brief Class that handles multitarget rendering (i.e. to different textures/surfaces) and
* later compositing into a single image (OpenGL flavour).
*/
#include "webgl_compositor.h"
#include "fullscreen_quad.h"
#include "utils.h"
#include <gal/color4d.h>
#include <cassert>
#include <memory>
#include <stdexcept>
#include <wx/log.h>
#include <wx/debug.h>
using namespace KIGFX;
WEBGL_COMPOSITOR::WEBGL_COMPOSITOR() :
m_initialized( false ),
m_curBuffer( 0 ),
m_mainFbo( 0 ),
m_depthBuffer( 0 ),
m_curFbo( DIRECT_RENDERING ),
m_currentAntialiasingMode( GAL_ANTIALIASING_MODE::AA_NONE ),
m_blitTexUniform( -1 )
{
m_antialiasing = std::make_unique<ANTIALIASING_NONE>( this );
}
void WEBGL_COMPOSITOR::initBlitShader()
{
// Simple blit shader for texture compositing
// Replaces legacy fixed-function GL_MODULATE texturing
static const char* blitVertexShader =
"#version 300 es\n"
"precision highp float;\n"
"\n"
"in vec4 a_vertex;\n"
"in vec4 a_texCoord0;\n"
"\n"
"out vec2 v_texCoord;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = a_vertex;\n"
" v_texCoord = a_texCoord0.xy;\n"
"}\n";
static const char* blitFragmentShader =
"#version 300 es\n"
"precision highp float;\n"
"\n"
"uniform sampler2D u_texture;\n"
"\n"
"in vec2 v_texCoord;\n"
"out vec4 fragColor;\n"
"\n"
"void main()\n"
"{\n"
" fragColor = texture( u_texture, v_texCoord );\n"
"}\n";
m_blitShader = std::make_unique<SHADER>();
m_blitShader->LoadShaderFromStrings( KIGFX::SHADER_TYPE_VERTEX, blitVertexShader );
m_blitShader->LoadShaderFromStrings( KIGFX::SHADER_TYPE_FRAGMENT, blitFragmentShader );
m_blitShader->Link();
checkGlError( "linking blit shader", __FILE__, __LINE__ );
m_blitTexUniform = m_blitShader->AddParameter( "u_texture" );
checkGlError( "getting blit texture uniform", __FILE__, __LINE__ );
m_blitShader->Use();
m_blitShader->SetParameter( m_blitTexUniform, 0 ); // Texture unit 0
m_blitShader->Deactivate();
}
WEBGL_COMPOSITOR::~WEBGL_COMPOSITOR()
{
if( m_initialized )
{
try
{
clean();
}
catch( const std::runtime_error& exc )
{
wxLogError( wxT( "Run time exception `%s` occurred in WEBGL_COMPOSITOR destructor." ),
exc.what() );
}
}
}
void WEBGL_COMPOSITOR::SetAntialiasingMode( GAL_ANTIALIASING_MODE aMode )
{
m_currentAntialiasingMode = aMode;
if( m_initialized )
clean();
}
GAL_ANTIALIASING_MODE WEBGL_COMPOSITOR::GetAntialiasingMode() const
{
return m_currentAntialiasingMode;
}
void WEBGL_COMPOSITOR::Initialize()
{
if( m_initialized )
return;
switch( m_currentAntialiasingMode )
{
case GAL_ANTIALIASING_MODE::AA_FAST:
m_antialiasing = std::make_unique<ANTIALIASING_SMAA>( this );
break;
case GAL_ANTIALIASING_MODE::AA_HIGHQUALITY:
m_antialiasing = std::make_unique<ANTIALIASING_SUPERSAMPLING>( this );
break;
default:
m_antialiasing = std::make_unique<ANTIALIASING_NONE>( this );
break;
}
VECTOR2I dims = m_antialiasing->GetInternalBufferSize();
assert( dims.x != 0 && dims.y != 0 );
GLint maxBufSize;
glGetIntegerv( GL_MAX_RENDERBUFFER_SIZE_EXT, &maxBufSize );
if( dims.x < 0 || dims.y < 0 || dims.x > maxBufSize || dims.y >= maxBufSize )
throw std::runtime_error( "Requested render buffer size is not supported" );
// We need framebuffer objects for drawing the screen contents
// Generate framebuffer and a depth buffer
glGenFramebuffersEXT( 1, &m_mainFbo );
checkGlError( "generating framebuffer", __FILE__, __LINE__ );
bindFb( m_mainFbo );
// Allocate memory for the depth buffer
// Attach the depth buffer to the framebuffer
glGenRenderbuffersEXT( 1, &m_depthBuffer );
checkGlError( "generating renderbuffer", __FILE__, __LINE__ );
glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, m_depthBuffer );
checkGlError( "binding renderbuffer", __FILE__, __LINE__ );
glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8, dims.x, dims.y );
checkGlError( "creating renderbuffer storage", __FILE__, __LINE__ );
glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_STENCIL_ATTACHMENT,
GL_RENDERBUFFER_EXT, m_depthBuffer );
checkGlError( "attaching renderbuffer", __FILE__, __LINE__ );
// Unbind the framebuffer, so by default all the rendering goes directly to the display
bindFb( DIRECT_RENDERING );
m_initialized = true;
// Initialize blit shader for texture compositing
initBlitShader();
// Initialize fullscreen quad VBO
GetFullscreenQuad().Initialize();
m_antialiasing->Init();
}
void WEBGL_COMPOSITOR::Resize( unsigned int aWidth, unsigned int aHeight )
{
if( m_initialized )
clean();
m_antialiasing->OnLostBuffers();
m_width = aWidth;
m_height = aHeight;
}
unsigned int WEBGL_COMPOSITOR::CreateBuffer()
{
return m_antialiasing->CreateBuffer();
}
unsigned int WEBGL_COMPOSITOR::CreateBuffer( VECTOR2I aDimensions )
{
assert( m_initialized );
int maxBuffers, maxTextureSize;
// Get the maximum number of buffers
glGetIntegerv( GL_MAX_COLOR_ATTACHMENTS, (GLint*) &maxBuffers );
if( (int) usedBuffers() >= maxBuffers )
{
throw std::runtime_error( "Cannot create more framebuffers. OpenGL rendering backend requires at "
"least 3 framebuffers. You may try to update/change your graphic drivers." );
}
glGetIntegerv( GL_MAX_TEXTURE_SIZE, (GLint*) &maxTextureSize );
if( maxTextureSize < (int) aDimensions.x || maxTextureSize < (int) aDimensions.y )
{
throw std::runtime_error( "Requested texture size is not supported. Could not create a buffer." );
}
// GL_COLOR_ATTACHMENTn are consecutive integers
GLuint attachmentPoint = GL_COLOR_ATTACHMENT0 + usedBuffers();
GLuint textureTarget;
// Generate the texture for the pixel storage
glActiveTexture( GL_TEXTURE0 );
glGenTextures( 1, &textureTarget );
checkGlError( "generating framebuffer texture target", __FILE__, __LINE__ );
glBindTexture( GL_TEXTURE_2D, textureTarget );
checkGlError( "binding framebuffer texture target", __FILE__, __LINE__ );
// Set texture parameters
// Note: glTexEnvf is not available in WebGL 2.0, texturing mode is handled by shaders
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, aDimensions.x, aDimensions.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr );
checkGlError( "creating framebuffer texture", __FILE__, __LINE__ );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
// Bind the texture to the specific attachment point, clear and rebind the screen
bindFb( m_mainFbo );
glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, attachmentPoint, GL_TEXTURE_2D, textureTarget, 0 );
// Check the status, exit if the framebuffer can't be created
GLenum status = glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT );
if( status != GL_FRAMEBUFFER_COMPLETE_EXT )
{
switch( status )
{
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
throw std::runtime_error( "The framebuffer attachment points are incomplete." );
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
throw std::runtime_error( "No images attached to the framebuffer." );
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
throw std::runtime_error( "The framebuffer does not have at least one image attached to it." );
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
throw std::runtime_error( "The framebuffer read buffer is incomplete." );
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
throw std::runtime_error( "The combination of internal formats of the attached images violates "
"an implementation-dependent set of restrictions." );
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT:
throw std::runtime_error( "GL_RENDERBUFFER_SAMPLES is not the same for all attached renderbuffers" );
case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT:
throw std::runtime_error( "Framebuffer incomplete layer targets errors." );
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
throw std::runtime_error( "Framebuffer attachments have different dimensions" );
default:
throw std::runtime_error( "Unknown error occurred when creating the framebuffer." );
}
}
ClearBuffer( COLOR4D::BLACK );
// Return to direct rendering (we were asked only to create a buffer, not switch to one)
bindFb( DIRECT_RENDERING );
// Store the new buffer
OPENGL_BUFFER buffer = { aDimensions, textureTarget, attachmentPoint };
m_buffers.push_back( buffer );
return usedBuffers();
}
GLenum WEBGL_COMPOSITOR::GetBufferTexture( unsigned int aBufferHandle )
{
wxCHECK( aBufferHandle > 0 && aBufferHandle <= usedBuffers(), 0 );
return m_buffers[aBufferHandle - 1].textureTarget;
}
void WEBGL_COMPOSITOR::SetBuffer( unsigned int aBufferHandle )
{
wxCHECK( m_initialized && aBufferHandle <= usedBuffers(), /* void */ );
// Either unbind the FBO for direct rendering, or bind the one with target textures
bindFb( aBufferHandle == DIRECT_RENDERING ? DIRECT_RENDERING : m_mainFbo );
// Switch the target texture
if( m_curFbo != DIRECT_RENDERING )
{
m_curBuffer = aBufferHandle - 1;
// WebGL 2.0/OpenGL ES 3.0: use glDrawBuffers instead of glDrawBuffer
// In WebGL 2.0, the draw buffer array index must match the attachment index.
// So for GL_COLOR_ATTACHMENTn, we need array[n] = GL_COLOR_ATTACHMENTn
GLenum attachmentPoint = m_buffers[m_curBuffer].attachmentPoint;
unsigned int attachmentIndex = attachmentPoint - GL_COLOR_ATTACHMENT0;
// Create draw buffers array with GL_NONE for all entries except the target
GLenum drawBuffers[16];
for( unsigned int i = 0; i <= attachmentIndex && i < 16; i++ )
drawBuffers[i] = GL_NONE;
drawBuffers[attachmentIndex] = attachmentPoint;
glDrawBuffers( attachmentIndex + 1, drawBuffers );
checkGlError( "setting draw buffer", __FILE__, __LINE__ );
glViewport( 0, 0, m_buffers[m_curBuffer].dimensions.x, m_buffers[m_curBuffer].dimensions.y );
}
else
{
glViewport( 0, 0, GetScreenSize().x, GetScreenSize().y );
}
}
void WEBGL_COMPOSITOR::ClearBuffer( const COLOR4D& aColor )
{
wxCHECK( m_initialized, /* void */ );
glClearColor( aColor.r, aColor.g, aColor.b, m_curFbo == DIRECT_RENDERING ? 1.0f : 0.0f );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
}
VECTOR2I WEBGL_COMPOSITOR::GetScreenSize() const
{
typedef VECTOR2I::coord_type coord_t;
wxASSERT( m_width <= static_cast<unsigned int>( std::numeric_limits<coord_t>::max() ) );
wxASSERT( m_height <= static_cast<unsigned int>( std::numeric_limits<coord_t>::max() ) );
return { static_cast<coord_t>( m_width ), static_cast<coord_t>( m_height ) };
}
void WEBGL_COMPOSITOR::Begin()
{
m_antialiasing->Begin();
}
void WEBGL_COMPOSITOR::DrawBuffer( unsigned int aBufferHandle )
{
m_antialiasing->DrawBuffer( aBufferHandle );
}
void WEBGL_COMPOSITOR::DrawBuffer( unsigned int aSourceHandle, unsigned int aDestHandle )
{
wxCHECK( m_initialized && aSourceHandle != 0 && aSourceHandle <= usedBuffers(), /* void */ );
wxCHECK( aDestHandle <= usedBuffers(), /* void */ );
// Switch to the destination buffer and blit the scene
SetBuffer( aDestHandle );
// Depth test has to be disabled to make transparency working
glDisable( GL_DEPTH_TEST );
// Use standard alpha blending for straight (non-premultiplied) alpha
// Note: GL_ONE would be for premultiplied alpha, but our FBOs use straight alpha
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
// Bind the source texture
glActiveTexture( GL_TEXTURE0 );
glBindTexture( GL_TEXTURE_2D, m_buffers[aSourceHandle - 1].textureTarget );
// Use blit shader and draw fullscreen quad
m_blitShader->Use();
GetFullscreenQuad().Draw();
m_blitShader->Deactivate();
}
void WEBGL_COMPOSITOR::Present()
{
m_antialiasing->Present();
}
void WEBGL_COMPOSITOR::bindFb( unsigned int aFb )
{
// Currently there are only 2 valid FBOs
wxASSERT( aFb == DIRECT_RENDERING || aFb == m_mainFbo );
if( m_curFbo != aFb )
{
glBindFramebufferEXT( GL_FRAMEBUFFER, aFb );
checkGlError( "switching framebuffer", __FILE__, __LINE__ );
m_curFbo = aFb;
}
}
void WEBGL_COMPOSITOR::clean()
{
wxCHECK( m_initialized, /* void */ );
bindFb( DIRECT_RENDERING );
for( const OPENGL_BUFFER& buffer : m_buffers )
glDeleteTextures( 1, &buffer.textureTarget );
m_buffers.clear();
if( glDeleteFramebuffersEXT )
glDeleteFramebuffersEXT( 1, &m_mainFbo );
if( glDeleteRenderbuffersEXT )
glDeleteRenderbuffersEXT( 1, &m_depthBuffer );
m_initialized = false;
}
int WEBGL_COMPOSITOR::GetAntialiasSupersamplingFactor() const
{
switch ( m_currentAntialiasingMode )
{
case GAL_ANTIALIASING_MODE::AA_HIGHQUALITY: return 2;
default: return 1;
}
}
VECTOR2D WEBGL_COMPOSITOR::GetAntialiasRenderingOffset() const
{
switch( m_currentAntialiasingMode )
{
case GAL_ANTIALIASING_MODE::AA_HIGHQUALITY: return VECTOR2D( 0.5, -0.5 );
default: return VECTOR2D( 0, 0 );
}
}

View file

@ -1,151 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013-2016 CERN
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/**
* @file opengl_compositor.h
* Handle multitarget rendering (ie. to different textures/surfaces) and later compositing
* into a single image (OpenGL flavor).
*/
#ifndef WEBGL_COMPOSITOR_H_
#define WEBGL_COMPOSITOR_H_
#include "kiglew.h" // Must be included first
#include <gal/compositor.h>
#include "webgl_antialiasing.h"
#include "fullscreen_quad.h"
#include "shader.h"
#include <gal/gal_display_options.h>
#include <deque>
#include <memory>
namespace KIGFX
{
class WEBGL_COMPOSITOR : public COMPOSITOR
{
public:
WEBGL_COMPOSITOR();
virtual ~WEBGL_COMPOSITOR();
/// @copydoc COMPOSITOR::Initialize()
virtual void Initialize() override;
/// @copydoc COMPOSITOR::Resize()
virtual void Resize( unsigned int aWidth, unsigned int aHeight ) override;
/// @copydoc COMPOSITOR::CreateBuffer()
virtual unsigned int CreateBuffer() override;
/// @copydoc COMPOSITOR::SetBuffer()
virtual void SetBuffer( unsigned int aBufferHandle ) override;
/// @copydoc COMPOSITOR::GetBuffer()
inline virtual unsigned int GetBuffer() const override
{
if( m_curFbo == DIRECT_RENDERING )
return DIRECT_RENDERING;
return m_curBuffer + 1;
}
/// @copydoc COMPOSITOR::ClearBuffer()
virtual void ClearBuffer( const COLOR4D& aColor ) override;
/// @copydoc COMPOSITOR::DrawBuffer()
virtual void DrawBuffer( unsigned int aBufferHandle ) override;
/// @copydoc COMPOSITOR::Begin()
virtual void Begin() override;
// @copydoc COMPOSITOR::Present()
virtual void Present() override;
// Constant used by glBindFramebuffer to turn off rendering to framebuffers
static const unsigned int DIRECT_RENDERING = 0;
VECTOR2I GetScreenSize() const;
GLenum GetBufferTexture( unsigned int aBufferHandle );
void DrawBuffer( unsigned int aSourceHandle, unsigned int aDestHandle );
unsigned int CreateBuffer( VECTOR2I aDimensions );
void SetAntialiasingMode( GAL_ANTIALIASING_MODE aMode ); // clears all buffers
GAL_ANTIALIASING_MODE GetAntialiasingMode() const;
int GetAntialiasSupersamplingFactor() const;
VECTOR2D GetAntialiasRenderingOffset() const;
protected:
/// Binds a specific Framebuffer Object.
void bindFb( unsigned int aFb );
/**
* Perform freeing of resources.
*/
void clean();
/// Returns number of used buffers
inline unsigned int usedBuffers()
{
return m_buffers.size();
}
// Buffers are simply textures storing a result of certain target rendering.
struct OPENGL_BUFFER
{
VECTOR2I dimensions;
GLuint textureTarget; ///< Main texture handle
GLuint attachmentPoint; ///< Point to which an image from texture is attached
};
bool m_initialized; ///< Initialization status flag
unsigned int m_curBuffer; ///< Currently used buffer handle
GLuint m_mainFbo; ///< Main FBO handle (storing all target textures)
GLuint m_depthBuffer; ///< Depth buffer handle
typedef std::deque<OPENGL_BUFFER> OPENGL_BUFFERS;
/// Stores information about initialized buffers
OPENGL_BUFFERS m_buffers;
/// Store the used FBO name in case there was more than one compositor used
GLuint m_curFbo;
GAL_ANTIALIASING_MODE m_currentAntialiasingMode;
std::unique_ptr<OPENGL_PRESENTOR> m_antialiasing;
// Blit shader for compositing (replacing legacy fixed-function pipeline)
std::unique_ptr<SHADER> m_blitShader;
int m_blitTexUniform; ///< Location of texture uniform
/**
* Initialize the blit shader for texture compositing.
*/
void initBlitShader();
};
} // namespace KIGFX
#endif /* COMPOSITOR_H_ */

File diff suppressed because it is too large Load diff

View file

@ -1,618 +0,0 @@
/*
* This program source code file is part of KICAD, a free EDA CAD application.
*
* Copyright (C) 2012 Torsten Hueter, torstenhtr <at> gmx.de
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2013-2017 CERN
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* Graphics Abstraction Layer (GAL) for OpenGL
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef OPENGLGAL_H_
#define OPENGLGAL_H_
// GAL imports
#include <gal/gal.h>
#include <gal/graphics_abstraction_layer.h>
#include <gal/gal_display_options.h>
#include "shader.h"
#include "vertex_manager.h"
#include "vertex_item.h"
#include "cached_container.h"
#include "noncached_container.h"
#include "webgl_compositor.h"
#include <gal/hidpi_gl_canvas.h>
#include <unordered_map>
#include <memory>
#include <wx/event.h>
#ifndef CALLBACK
#define CALLBACK
#endif
///< The default number of points for circle approximation
#define SEG_PER_CIRCLE_COUNT 64
struct bitmap_glyph;
namespace KIGFX
{
class SHADER;
class GL_BITMAP_CACHE;
/**
* OpenGL implementation of the Graphics Abstraction Layer.
*
* This is a direct OpenGL-implementation and uses low-level graphics primitives like triangles
* and quads. The purpose is to provide a fast graphics interface, that takes advantage of modern
* graphics card GPUs. All methods here benefit thus from the hardware acceleration.
*/
class GAL_API WEBGL_GAL : public GAL, public HIDPI_GL_CANVAS
{
public:
/**
* @param aParent is the wxWidgets immediate wxWindow parent of this object.
*
* @param aMouseListener is the wxEvtHandler that should receive the mouse events,
* this can be can be any wxWindow, but is often a wxFrame container.
*
* @param aPaintListener is the wxEvtHandler that should receive the paint
* event. This can be any wxWindow, but is often a derived instance
* of this class or a containing wxFrame. The "paint event" here is
* a wxCommandEvent holding EVT_GAL_REDRAW, as sent by PostPaint().
*
* @param aName is the name of this window for use by wxWindow::FindWindowByName()
*/
WEBGL_GAL( const KIGFX::VC_SETTINGS& aVcSettings, GAL_DISPLAY_OPTIONS& aDisplayOptions,
wxWindow* aParent,
wxEvtHandler* aMouseListener = nullptr, wxEvtHandler* aPaintListener = nullptr,
const wxString& aName = wxT( "GLCanvas" ) );
~WEBGL_GAL();
/**
* Checks OpenGL features.
*
* @param aOptions
* @return wxEmptyString if OpenGL 2.1 or greater is available, otherwise returns error message
*/
static wxString CheckFeatures( GAL_DISPLAY_OPTIONS& aOptions );
bool IsOpenGlEngine() override { return true; }
/// @copydoc GAL::IsInitialized()
bool IsInitialized() const override
{
// is*Initialized flags, but it is enough for OpenGL to show up
return IsShownOnScreen() && !GetClientRect().IsEmpty();
}
///< @copydoc GAL::IsVisible()
bool IsVisible() const override
{
return IsShownOnScreen() && !GetClientRect().IsEmpty();
}
void SetMinLineWidth( float aLineWidth ) override;
// ---------------
// Drawing methods
// ---------------
/// @copydoc GAL::DrawLine()
void DrawLine( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) override;
/// @copydoc GAL::DrawSegment()
void DrawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint,
double aWidth ) override;
/// @copydoc GAL::DrawSegmentChain()
void DrawSegmentChain( const std::vector<VECTOR2D>& aPointList, double aWidth ) override;
void DrawSegmentChain( const SHAPE_LINE_CHAIN& aLineChain, double aWidth ) override;
/// @copydoc GAL::DrawCircle()
void DrawCircle( const VECTOR2D& aCenterPoint, double aRadius ) override;
/// @copydoc GAL::DrawHoleWall()
void DrawHoleWall( const VECTOR2D& aCenterPoint, double aHoleRadius,
double aWallWidth ) override;
/// @copydoc GAL::DrawArc()
void DrawArc( const VECTOR2D& aCenterPoint, double aRadius, const EDA_ANGLE& aStartAngle,
const EDA_ANGLE& aAngle ) override;
/// @copydoc GAL::DrawArcSegment()
void DrawArcSegment( const VECTOR2D& aCenterPoint, double aRadius, const EDA_ANGLE& aStartAngle,
const EDA_ANGLE& aAngle, double aWidth, double aMaxError ) override;
/// @copydoc GAL::DrawRectangle()
void DrawRectangle( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint ) override;
/// @copydoc GAL::DrawPolyline()
void DrawPolyline( const std::deque<VECTOR2D>& aPointList ) override;
void DrawPolyline( const std::vector<VECTOR2D>& aPointList ) override;
void DrawPolyline( const VECTOR2D aPointList[], int aListSize ) override;
void DrawPolyline( const SHAPE_LINE_CHAIN& aLineChain ) override;
/// @copydoc GAL::DrawPolylines()
void DrawPolylines( const std::vector<std::vector<VECTOR2D>>& aPointLists ) override;
/// @copydoc GAL::DrawPolygon()
void DrawPolygon( const std::deque<VECTOR2D>& aPointList ) override;
void DrawPolygon( const VECTOR2D aPointList[], int aListSize ) override;
void DrawPolygon( const SHAPE_POLY_SET& aPolySet, bool aStrokeTriangulation = false ) override;
void DrawPolygon( const SHAPE_LINE_CHAIN& aPolySet ) override;
/// @copydoc GAL::DrawGlyph()
virtual void DrawGlyph( const KIFONT::GLYPH& aGlyph, int aNth, int aTotal ) override;
/// @copydoc GAL::DrawGlyphs()
virtual void DrawGlyphs( const std::vector<std::unique_ptr<KIFONT::GLYPH>>& aGlyphs ) override;
/// @copydoc GAL::DrawCurve()
void DrawCurve( const VECTOR2D& startPoint, const VECTOR2D& controlPointA,
const VECTOR2D& controlPointB, const VECTOR2D& endPoint,
double aFilterValue = 0.0 ) override;
/// @copydoc GAL::DrawBitmap()
void DrawBitmap( const BITMAP_BASE& aBitmap, double alphaBlend = 1.0 ) override;
/// @copydoc GAL::BitmapText()
void BitmapText( const wxString& aText, const VECTOR2I& aPosition,
const EDA_ANGLE& aAngle ) override;
/// @copydoc GAL::DrawGrid()
void DrawGrid() override;
// --------------
// Screen methods
// --------------
/// @brief Resizes the canvas.
void ResizeScreen( int aWidth, int aHeight ) override;
/// @brief Shows/hides the GAL canvas
bool Show( bool aShow ) override;
/// @copydoc GAL::GetSwapInterval()
int GetSwapInterval() const override { return m_swapInterval; };
/// @copydoc GAL::Flush()
void Flush() override;
/// @copydoc GAL::ClearScreen()
void ClearScreen( ) override;
// --------------
// Transformation
// --------------
/// @copydoc GAL::Transform()
void Transform( const MATRIX3x3D& aTransformation ) override;
/// @copydoc GAL::Rotate()
void Rotate( double aAngle ) override;
/// @copydoc GAL::Translate()
void Translate( const VECTOR2D& aTranslation ) override;
/// @copydoc GAL::Scale()
void Scale( const VECTOR2D& aScale ) override;
/// @copydoc GAL::Save()
void Save() override;
/// @copydoc GAL::Restore()
void Restore() override;
// --------------------------------------------
// Group methods
// ---------------------------------------------
/// @copydoc GAL::BeginGroup()
int BeginGroup() override;
/// @copydoc GAL::EndGroup()
void EndGroup() override;
/// @copydoc GAL::DrawGroup()
void DrawGroup( int aGroupNumber ) override;
/// @copydoc GAL::ChangeGroupColor()
void ChangeGroupColor( int aGroupNumber, const COLOR4D& aNewColor ) override;
/// @copydoc GAL::ChangeGroupDepth()
void ChangeGroupDepth( int aGroupNumber, int aDepth ) override;
/// @copydoc GAL::DeleteGroup()
void DeleteGroup( int aGroupNumber ) override;
/// @copydoc GAL::ClearCache()
void ClearCache() override;
// --------------------------------------------------------
// Handling the world <-> screen transformation
// --------------------------------------------------------
/// @copydoc GAL::SetTarget()
void SetTarget( RENDER_TARGET aTarget ) override;
/// @copydoc GAL::GetTarget()
RENDER_TARGET GetTarget() const override;
/// @copydoc GAL::ClearTarget()
void ClearTarget( RENDER_TARGET aTarget ) override;
/// @copydoc GAL::HasTarget()
virtual bool HasTarget( RENDER_TARGET aTarget ) override;
/// @copydoc GAL::SetNegativeDrawMode()
void SetNegativeDrawMode( bool aSetting ) override {}
/// @copydoc GAL::StartDiffLayer()
void StartDiffLayer() override;
//
/// @copydoc GAL::EndDiffLayer()
void EndDiffLayer() override;
void ComputeWorldScreenMatrix() override;
// -------
// Cursor
// -------
/// @copydoc GAL::SetNativeCursorStyle()
bool SetNativeCursorStyle( KICURSOR aCursor, bool aHiDPI ) override;
/// @copydoc GAL::DrawCursor()
void DrawCursor( const VECTOR2D& aCursorPosition ) override;
/**
* Post an event to #m_paint_listener.
*
* A post is used so that the actual drawing function can use a device context type that
* is not specific to the wxEVT_PAINT event, just by changing the PostPaint code.
*/
void PostPaint( wxPaintEvent& aEvent );
void SetMouseListener( wxEvtHandler* aMouseListener )
{
m_mouseListener = aMouseListener;
}
void SetPaintListener( wxEvtHandler* aPaintListener )
{
m_paintListener = aPaintListener;
}
void EnableDepthTest( bool aEnabled = false ) override;
bool IsContextLocked() override
{
return m_isContextLocked;
}
void LockContext( int aClientCookie ) override;
void UnlockContext( int aClientCookie ) override;
/// @copydoc GAL::BeginDrawing()
void BeginDrawing() override;
/// @copydoc GAL::EndDrawing()
void EndDrawing() override;
///< Parameters passed to the GLU tesselator
struct TessParams
{
/// Manager used for storing new vertices
VERTEX_MANAGER* vboManager;
/// Intersect points, that have to be freed after tessellation
std::deque<std::shared_ptr<GLdouble>>& intersectPoints;
};
private:
/// Super class definition
typedef GAL super;
static wxGLContext* m_glMainContext; ///< Parent OpenGL context
wxGLContext* m_glPrivContext; ///< Canvas-specific OpenGL context
int m_swapInterval; ///< Used to store swap interval information
static int m_instanceCounter; ///< GL GAL instance counter
wxEvtHandler* m_mouseListener;
wxEvtHandler* m_paintListener;
static GLuint g_fontTexture; ///< Bitmap font texture handle (shared)
// Vertex buffer objects related fields
typedef std::unordered_map< unsigned int, std::shared_ptr<VERTEX_ITEM> > GROUPS_MAP;
GROUPS_MAP m_groups; ///< Stores information about VBO objects (groups)
unsigned int m_groupCounter; ///< Counter used for generating keys for groups
VERTEX_MANAGER* m_currentManager; ///< Currently used VERTEX_MANAGER (for storing
///< VERTEX_ITEMs).
VERTEX_MANAGER* m_cachedManager; ///< Container for storing cached VERTEX_ITEMs
VERTEX_MANAGER* m_nonCachedManager; ///< Container for storing non-cached VERTEX_ITEMs
VERTEX_MANAGER* m_overlayManager; ///< Container for storing overlaid VERTEX_ITEMs
/// Container for storing temp (diff mode) VERTEX_ITEMs
VERTEX_MANAGER* m_tempManager;
// Framebuffer & compositing
WEBGL_COMPOSITOR* m_compositor; ///< Handles multiple rendering targets
unsigned int m_mainBuffer; ///< Main rendering target
unsigned int m_overlayBuffer; ///< Auxiliary rendering target (for menus etc.)
unsigned int m_tempBuffer; ///< Temporary rendering target (for diffing etc.)
RENDER_TARGET m_currentTarget; ///< Current rendering target
// Shader
/// There is only one shader used for different objects.
SHADER* m_shader;
// Internal flags
bool m_isFramebufferInitialized; ///< Are the framebuffers initialized?
static bool m_isBitmapFontLoaded; ///< Is the bitmap font texture loaded?
bool m_isBitmapFontInitialized; ///< Is the shader set to use bitmap fonts?
bool m_isInitialized; ///< Basic initialization flag, has to be
///< done when the window is visible
bool m_isGrouping; ///< Was a group started?
bool m_isContextLocked; ///< Used for assertion checking
int m_lockClientCookie;
GLint ufm_worldPixelSize;
GLint ufm_screenPixelSize;
GLint ufm_pixelSizeMultiplier;
GLint ufm_antialiasingOffset;
GLint ufm_minLinePixelWidth;
GLint ufm_fontTexture;
GLint ufm_fontTextureWidth;
GLint ufm_modelViewProjectionMatrix; ///< MVP matrix uniform location
/// Current model-view-projection matrix (column-major for OpenGL)
float m_mvpMatrix[16];
/// wx cursor showing the current native cursor.
WX_CURSOR_TYPE m_currentwxCursor;
std::unique_ptr<GL_BITMAP_CACHE> m_bitmapCache;
// Polygon tesselation
GLUtesselator* m_tesselator;
std::deque<std::shared_ptr<GLdouble>> m_tessIntersects;
/// @copydoc GAL::BeginUpdate()
void beginUpdate() override;
/// @copydoc GAL::EndUpdate()
void endUpdate() override;
///< Update handler for OpenGL settings
bool updatedGalDisplayOptions( const GAL_DISPLAY_OPTIONS& aOptions ) override;
/**
* Draw a quad for the line.
*
* @param aStartPoint is the start point of the line.
* @param aEndPoint is the end point of the line.
* @param aReserve if set to false, call reserveLineQuads beforehand
* to reserve the right amount of vertices.
*/
void drawLineQuad( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint,
bool aReserve = true );
/**
* Reserve specified number of line quads.
*
* @param aLineCount the number of line quads to reserve.
*/
void reserveLineQuads( const int aLineCount );
/**
* Draw a semicircle.
*
* Depending on settings (m_isStrokeEnabled & isFilledEnabled) it runs the proper function
* (drawStrokedSemiCircle or drawFilledSemiCircle).
*
* @param aCenterPoint is the center point.
* @param aRadius is the radius of the semicircle.
* @param aAngle is the angle of the semicircle.
*
*/
void drawSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle );
/**
*Draw a filled semicircle.
*
* @param aCenterPoint is the center point.
* @param aRadius is the radius of the semicircle.
* @param aAngle is the angle of the semicircle.
*
*/
void drawFilledSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle );
/**
* Draw a stroked semicircle.
*
* @param aCenterPoint is the center point.
* @param aRadius is the radius of the semicircle.
* @param aAngle is the angle of the semicircle.
* @param aReserve if set to false, reserve 3 vertices for each semicircle.
*
*/
void drawStrokedSemiCircle( const VECTOR2D& aCenterPoint, double aRadius, double aAngle,
bool aReserve = true );
/**
* Internal method for circle drawing.
*
* @param aReserve if set to false, reserve 3 vertices for each circle.
*/
void drawCircle( const VECTOR2D& aCenterPoint, double aRadius, bool aReserve = true );
/**
* Generic way of drawing a polyline stored in different containers.
*
* @param aPointGetter is a function to obtain coordinates of n-th vertex.
* @param aPointCount is the number of points to be drawn.
* @param aReserve if set to false, reserve aPointCount - 1 line quads.
*/
void drawPolyline( const std::function<VECTOR2D( int )>& aPointGetter, int aPointCount,
bool aReserve = true );
/**
* Generic way of drawing a chain of segments stored in different containers.
*
* @param aPointGetter is a function to obtain coordinates of n-th vertex.
* @param aPointCount is the number of points to be drawn.
* @param aReserve if set to false, do not reserve vertices internally.
*/
void drawSegmentChain( const std::function<VECTOR2D( int )>& aPointGetter, int aPointCount,
double aWidth, bool aReserve = true );
/**
* Internal method for segment drawing
*/
void drawSegment( const VECTOR2D& aStartPoint, const VECTOR2D& aEndPoint, double aWidth,
bool aReserve = true );
/**
* Draw a filled polygon. It does not need the last point to have the same coordinates
* as the first one.
*
* @param aPoints is the vertices data (3 coordinates: x, y, z).
* @param aPointCount is the number of points.
*/
void drawPolygon( GLdouble* aPoints, int aPointCount );
/**
* Draw a set of polygons with a cached triangulation. Way faster than drawPolygon.
*
* @param aStrokeTriangulation indicates the triangulation should be stroked rather than
* filled. Used for debugging.
*/
void drawTriangulatedPolyset( const SHAPE_POLY_SET& aPoly, bool aStrokeTriangulation );
/**
* Draw a single character using bitmap font.
*
* Its main purpose is to be used in BitmapText() function.
*
* @param aChar is the character to be drawn.
* @return Width of the drawn glyph.
* @param aReserve if set to false, reserve 6 vertices for each character.
*/
int drawBitmapChar( unsigned long aChar, bool aReserve = true );
/**
* Draw an overbar over the currently drawn text.
*
* Its main purpose is to be used in BitmapText() function.
* This method requires appropriate scaling to be applied (as is done in BitmapText() function).
* The current X coordinate will be the overbar ending.
*
* @param aLength is the width of the overbar.
* @param aHeight is the height for the overbar.
* @param aReserve if set to false, reserve 6 vertices for each overbar.
*/
void drawBitmapOverbar( double aLength, double aHeight, bool aReserve = true );
/**
* Compute a size of text drawn using bitmap font with current text setting applied.
*
* @param aText is the text to be drawn.
* @return Pair containing text bounding box and common Y axis offset. The values are expressed
* as a number of pixels on the bitmap font texture and need to be scaled before drawing.
*/
std::pair<VECTOR2D, float> computeBitmapTextSize( const UTF8& aText ) const;
// Event handling
/**
* This is the OnPaint event handler.
*
* @param aEvent is the OnPaint event.
*/
void onPaint( wxPaintEvent& aEvent );
/**
* Skip the mouse event to the parent.
*
* @param aEvent is the mouse event.
*/
void skipMouseEvent( wxMouseEvent& aEvent );
/**
* Skip the gesture event to the parent.
*
* @param aEvent is the gesture event.
*/
void skipGestureEvent( wxGestureEvent& aEvent );
/**
* Give the correct cursor image when the native widget asks for it.
*
* @param aEvent is the cursor event to plac the cursor into.
*/
void onSetNativeCursor( wxSetCursorEvent& aEvent );
/**
* Blit cursor into the current screen.
*/
void blitCursor();
/**
* Return a valid key that can be used as a new group number.
*
* @return An unique group number that is not used by any other group.
*/
unsigned int getNewGroupNumber();
/**
* Compute the angle step when drawing arcs/circles approximated with lines.
*/
double calcAngleStep( double aRadius ) const
{
// Bigger arcs need smaller alpha increment to make them look smooth
return std::min( 1e6 / aRadius, 2.0 * M_PI / SEG_PER_CIRCLE_COUNT );
}
double getWorldPixelSize() const;
VECTOR2D getScreenPixelSize() const;
/**
* Set up the shader parameters for OpenGL rendering.
* This method initializes all the uniform parameter locations
* after the shader has been linked.
*/
void setupShaderParameters();
/**
* Basic OpenGL initialization and feature checks.
*
* @throw std::runtime_error if any of the OpenGL feature checks failed
*/
void init();
};
} // namespace KIGFX
#endif // OPENGLGAL_H_