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:
parent
5e74a001e7
commit
37d973f064
50 changed files with 403 additions and 28977 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -55,3 +55,4 @@ wxwidgets-clean/
|
||||||
*.log
|
*.log
|
||||||
*.tmp
|
*.tmp
|
||||||
output/
|
output/
|
||||||
|
*.d
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# Use ARM64-native image for Apple Silicon (M1/M2/M3/M4)
|
# 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
|
# Install build tools required for KiCad WASM build
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
|
|
|
||||||
2
kicad
2
kicad
|
|
@ -1 +1 @@
|
||||||
Subproject commit 0e07ba164cb533c60d2904ca8c3a329182dc843a
|
Subproject commit 1b5bb125d2fa94944641bf73c1d51057042292b5
|
||||||
|
|
@ -8,6 +8,11 @@
|
||||||
# Uses a Makefile with direct em++ calls (like build-wasm-test.sh)
|
# Uses a Makefile with direct em++ calls (like build-wasm-test.sh)
|
||||||
# to avoid emcmake Python 3.10+ requirement.
|
# 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)
|
# Redirect all output to a log file (re-execs script with redirection)
|
||||||
source "$(dirname "$0")/common/logging.sh"
|
source "$(dirname "$0")/common/logging.sh"
|
||||||
|
|
@ -37,18 +42,19 @@ fi
|
||||||
echo " Emscripten: $(em++ --version 2>&1 | head -1)"
|
echo " Emscripten: $(em++ --version 2>&1 | head -1)"
|
||||||
|
|
||||||
# Parse arguments
|
# Parse arguments
|
||||||
|
# Default: clean build to avoid stale object file issues (header deps not tracked in old builds)
|
||||||
DEBUG_BUILD=0
|
DEBUG_BUILD=0
|
||||||
CLEAN_BUILD=0
|
CLEAN_BUILD=1
|
||||||
|
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
if [ "$arg" = "--debug" ]; then
|
if [ "$arg" = "--debug" ]; then
|
||||||
DEBUG_BUILD=1
|
DEBUG_BUILD=1
|
||||||
elif [ "$arg" = "--clean" ]; then
|
elif [ "$arg" = "--no-clean" ]; then
|
||||||
CLEAN_BUILD=1
|
CLEAN_BUILD=0
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Build using Makefile
|
# Build using Makefile (compiles WebGL sources directly from kicad/)
|
||||||
cd "$TEST_DIR"
|
cd "$TEST_DIR"
|
||||||
|
|
||||||
if [ "$CLEAN_BUILD" = "1" ]; then
|
if [ "$CLEAN_BUILD" = "1" ]; then
|
||||||
|
|
@ -62,6 +68,7 @@ fi
|
||||||
rm -f "$OUTPUT_DIR"/*.js "$OUTPUT_DIR"/*.wasm 2>/dev/null || true
|
rm -f "$OUTPUT_DIR"/*.js "$OUTPUT_DIR"/*.wasm 2>/dev/null || true
|
||||||
|
|
||||||
# Generate shaders (converts GLSL 1.20 to GLSL ES 3.00)
|
# Generate shaders (converts GLSL 1.20 to GLSL ES 3.00)
|
||||||
|
# TODO: Eventually these should come from KiCad's build
|
||||||
echo ""
|
echo ""
|
||||||
echo "Generating WebGL shaders..."
|
echo "Generating WebGL shaders..."
|
||||||
python3 generate_shaders.py
|
python3 generate_shaders.py
|
||||||
|
|
|
||||||
|
|
@ -239,7 +239,7 @@ emcmake cmake "${KICAD_DIR}" \
|
||||||
-DCMAKE_POLICY_VERSION_MINIMUM=3.5 \
|
-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_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_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}" \
|
-DCMAKE_PREFIX_PATH="${SYSROOT};${WX_BUILD}" \
|
||||||
-DwxWidgets_CONFIG_EXECUTABLE="${WX_BUILD}/wx-config" \
|
-DwxWidgets_CONFIG_EXECUTABLE="${WX_BUILD}/wx-config" \
|
||||||
\
|
\
|
||||||
|
|
|
||||||
|
|
@ -320,24 +320,18 @@ build_webgl() {
|
||||||
log_header "Building WebGL Test"
|
log_header "Building WebGL Test"
|
||||||
|
|
||||||
if [ ! -f "$SCRIPT_DIR/build-gal-webgl-test.sh" ]; then
|
if [ ! -f "$SCRIPT_DIR/build-gal-webgl-test.sh" ]; then
|
||||||
log_step "WebGL build script not found (Phase 2)"
|
log_step "WebGL build script not found"
|
||||||
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)"
|
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Delegate to build-gal-webgl-test.sh (handles Emscripten check, clean build, etc.)
|
||||||
log_step "Running scripts/build-gal-webgl-test.sh..."
|
log_step "Running scripts/build-gal-webgl-test.sh..."
|
||||||
|
|
||||||
if "$SCRIPT_DIR/build-gal-webgl-test.sh"; then
|
if "$SCRIPT_DIR/build-gal-webgl-test.sh"; then
|
||||||
log_success "WebGL build succeeded"
|
log_success "WebGL build succeeded"
|
||||||
else
|
else
|
||||||
log_error "WebGL build failed (Emscripten may need Python 3.10+)"
|
log_error "WebGL build failed"
|
||||||
log_step "Try running inside Docker or with a compatible emsdk"
|
log_step "Check if KiCad is built first: docker/build.sh"
|
||||||
return 0 # Don't fail the whole test, just skip WebGL
|
return 0 # Don't fail the whole test, just skip WebGL
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
|
||||||
340
scripts/test-gal-webgl.sh
Executable file
340
scripts/test-gal-webgl.sh
Executable 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
|
||||||
|
|
@ -35,17 +35,18 @@ KICAD_INCLUDES = -I../native \
|
||||||
-Igenerated \
|
-Igenerated \
|
||||||
-I$(KICAD_ROOT)/include \
|
-I$(KICAD_ROOT)/include \
|
||||||
-I$(KICAD_ROOT)/include/gal \
|
-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)/common \
|
||||||
-I$(KICAD_ROOT)/libs/kimath/include \
|
-I$(KICAD_ROOT)/libs/kimath/include \
|
||||||
|
-I$(KICAD_ROOT)/libs/kimath/glu_tess \
|
||||||
-I$(KICAD_ROOT)/libs/kiplatform/include \
|
-I$(KICAD_ROOT)/libs/kiplatform/include \
|
||||||
-I$(KICAD_ROOT)/libs/core/include \
|
-I$(KICAD_ROOT)/libs/core/include \
|
||||||
-I$(KICAD_ROOT)/thirdparty/glm \
|
-I$(KICAD_ROOT)/thirdparty/glm \
|
||||||
-I$(KICAD_ROOT)/thirdparty/nlohmann_json \
|
-I$(KICAD_ROOT)/thirdparty/nlohmann_json \
|
||||||
-I$(KICAD_ROOT)/thirdparty/thread-pool \
|
-I$(KICAD_ROOT)/thirdparty/thread-pool \
|
||||||
-I$(KICAD_ROOT)/thirdparty/clipper2/Clipper2Lib/include \
|
-I$(KICAD_ROOT)/thirdparty/clipper2/Clipper2Lib/include \
|
||||||
-I$(SYSROOT)/include \
|
-I$(SYSROOT)/include
|
||||||
-Iwebgl
|
|
||||||
|
|
||||||
# Debug or Release build
|
# Debug or Release build
|
||||||
ifdef DEBUG
|
ifdef DEBUG
|
||||||
|
|
@ -56,7 +57,9 @@ else
|
||||||
DEBUG_LDFLAGS =
|
DEBUG_LDFLAGS =
|
||||||
endif
|
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
|
# Emscripten flags
|
||||||
BASE_LDFLAGS = -sALLOW_MEMORY_GROWTH=1 \
|
BASE_LDFLAGS = -sALLOW_MEMORY_GROWTH=1 \
|
||||||
|
|
@ -88,21 +91,14 @@ HTML_TEMPLATE = $(TOOLS_ROOT)/template.html
|
||||||
# Source files
|
# Source files
|
||||||
MAIN_SRCS = gal_webgl_test.cpp wasm_stubs.cpp
|
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)
|
# Test scenario sources (shared with native test)
|
||||||
# Include ALL scenario files - they should use the abstract GAL interface only.
|
# 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).
|
# The harness decides whether to use OPENGL_GAL (native) or WEBGL_GAL (wasm).
|
||||||
SCENARIO_SRCS = ../scenarios/gal_test_scenarios.cpp \
|
SCENARIO_SRCS = ../scenarios/gal_test_scenarios.cpp \
|
||||||
$(wildcard ../scenarios/scenario_*.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)
|
# 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 \
|
SHADER_SRCS = generated/glsl_kicad_frag.cpp \
|
||||||
generated/glsl_kicad_vert.cpp \
|
generated/glsl_kicad_vert.cpp \
|
||||||
generated/glsl_smaa_base.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_frag.cpp \
|
||||||
generated/glsl_smaa_pass_3_vert.cpp
|
generated/glsl_smaa_pass_3_vert.cpp
|
||||||
|
|
||||||
# WebGL GAL sources (ported from OpenGL GAL)
|
# KiCad GAL sources needed for linking (base GAL classes)
|
||||||
WEBGL_SRCS = webgl/webgl_gal.cpp \
|
KICAD_GAL_SRCS = $(KICAD_ROOT)/common/gal/hidpi_gl_canvas.cpp \
|
||||||
webgl/vertex_manager.cpp \
|
$(KICAD_ROOT)/common/gal/graphics_abstraction_layer.cpp \
|
||||||
webgl/vertex_container.cpp \
|
$(KICAD_ROOT)/common/gal/gal_display_options.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
|
|
||||||
|
|
||||||
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)
|
OBJS = $(SRCS:.cpp=.o)
|
||||||
|
DEPS = $(OBJS:.o=.d)
|
||||||
|
|
||||||
# Target
|
# Target
|
||||||
TARGET = $(OUTPUT_DIR)/gal_webgl_test.js
|
TARGET = $(OUTPUT_DIR)/gal_webgl_test.js
|
||||||
|
|
@ -146,14 +151,21 @@ $(OUTPUT_DIR):
|
||||||
%.o: %.cpp
|
%.o: %.cpp
|
||||||
$(CXX) -c $(CXXFLAGS) $< -o $@
|
$(CXX) -c $(CXXFLAGS) $< -o $@
|
||||||
|
|
||||||
|
# Link test harness (compiles WebGL sources directly from kicad/)
|
||||||
$(TARGET): $(OBJS)
|
$(TARGET): $(OBJS)
|
||||||
|
@echo "Linking $(words $(OBJS)) objects..."
|
||||||
$(CXX) $(OBJS) $(LDFLAGS) --pre-js $(JS) -o $@
|
$(CXX) $(OBJS) $(LDFLAGS) --pre-js $(JS) -o $@
|
||||||
cp gal_webgl_test.html $(OUTPUT_DIR)/
|
cp gal_webgl_test.html $(OUTPUT_DIR)/
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(OBJS)
|
rm -f $(OBJS) $(DEPS)
|
||||||
rm -f $(OUTPUT_DIR)/gal_webgl_test.js
|
rm -f $(OUTPUT_DIR)/gal_webgl_test.js
|
||||||
rm -f $(OUTPUT_DIR)/gal_webgl_test.wasm
|
rm -f $(OUTPUT_DIR)/gal_webgl_test.wasm
|
||||||
rm -f $(OUTPUT_DIR)/gal_webgl_test.html
|
rm -f $(OUTPUT_DIR)/gal_webgl_test.html
|
||||||
|
# Also clean object files in scenario directories
|
||||||
|
rm -f ../scenarios/*.o ../scenarios/*.d
|
||||||
|
|
||||||
.PHONY: all clean
|
.PHONY: all clean
|
||||||
|
|
||||||
|
# Include generated dependency files (silently ignore if missing on first build)
|
||||||
|
-include $(DEPS)
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
@ -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 */
|
|
||||||
}
|
|
||||||
|
|
@ -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_ */
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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_ */
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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_ */
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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_ */
|
|
||||||
|
|
@ -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 ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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 */
|
|
||||||
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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 */
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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_ */
|
|
||||||
|
|
@ -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_
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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_ */
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
@ -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_ */
|
|
||||||
|
|
@ -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 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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 */
|
|
||||||
|
|
@ -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¶m}
|
|
||||||
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_ */
|
|
||||||
|
|
@ -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()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
@ -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_ */
|
|
||||||
|
|
@ -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 );
|
|
||||||
}
|
|
||||||
|
|
@ -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_ */
|
|
||||||
|
|
@ -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 );
|
|
||||||
}
|
|
||||||
|
|
@ -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_ */
|
|
||||||
|
|
@ -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 );
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
@ -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_
|
|
||||||
Loading…
Reference in a new issue