Compare commits
No commits in common. "8eda3e5969bd14a9e2b5666dbac4502e6c5b7370" and "78cafe656684be32022bc9202628f9d3633e216d" have entirely different histories.
8eda3e5969
...
78cafe6566
4 changed files with 185 additions and 375 deletions
|
@ -1,7 +1,5 @@
|
||||||
import std/strformat
|
import std/strformat
|
||||||
import std/hashes
|
import std/hashes
|
||||||
import ./slice_mem
|
|
||||||
export slice_mem
|
|
||||||
|
|
||||||
const header_size = when not defined(release):
|
const header_size = when not defined(release):
|
||||||
sizeof(pointer)*2 + sizeof(Natural)
|
sizeof(pointer)*2 + sizeof(Natural)
|
||||||
|
@ -9,9 +7,10 @@ else:
|
||||||
sizeof(pointer)
|
sizeof(pointer)
|
||||||
|
|
||||||
type ArrRef*[T] = ref object
|
type ArrRef*[T] = ref object
|
||||||
byte_len: int
|
endp: pointer
|
||||||
when not defined(release):
|
when not defined(release):
|
||||||
# to see in the debugger
|
# to see in the debugger
|
||||||
|
size_bytes: Natural
|
||||||
arr_ptr: ptr T
|
arr_ptr: ptr T
|
||||||
arr: UncheckedArray[T]
|
arr: UncheckedArray[T]
|
||||||
|
|
||||||
|
@ -22,31 +21,35 @@ proc newArrRef*[T](size: Natural): ArrRef[T] =
|
||||||
result = cast[ArrRef[T]](r)
|
result = cast[ArrRef[T]](r)
|
||||||
else:
|
else:
|
||||||
unsafeNew(result, size * sizeof(T) + header_size)
|
unsafeNew(result, size * sizeof(T) + header_size)
|
||||||
result.byte_len = size * sizeof(T)
|
result.endp = addr(result.arr[size])
|
||||||
when not defined(release):
|
when not defined(release):
|
||||||
|
result.size_bytes = size * sizeof(T)
|
||||||
result.arr_ptr = result.arr[0].addr
|
result.arr_ptr = result.arr[0].addr
|
||||||
|
|
||||||
template len*[T](a: ArrRef[T]): Natural =
|
template len*[T](a: ArrRef[T]): Natural =
|
||||||
a.byte_len div sizeof(T)
|
((cast[int](a.endp) -% cast[int](a)) -% header_size) div sizeof(T)
|
||||||
|
|
||||||
template byteLen*[T](a: ArrRef[T]): Natural =
|
template byteLen*[T](a: ArrRef[T]): Natural =
|
||||||
a.byte_len
|
((cast[int](a.endp) -% cast[int](a)) -% header_size)
|
||||||
|
|
||||||
template low*[T](a: ArrRef[T]): Natural = 0
|
template low*[T](a: ArrRef[T]): Natural = 0
|
||||||
|
|
||||||
template high*[T](a: ArrRef[T]): int = a.len - 1
|
template high*[T](a: ArrRef[T]): int = a.len - 1
|
||||||
|
|
||||||
|
template rangeError[T](a: ArrRef[T], i: Natural) =
|
||||||
|
raise newException(RangeDefect, &"index out of range: {i} >= {a.len}")
|
||||||
|
|
||||||
proc `[]`*[T](a: ArrRef[T], i: Natural): var T =
|
proc `[]`*[T](a: ArrRef[T], i: Natural): var T =
|
||||||
|
let p = cast[int](a) +% header_size +% sizeof(T) * i
|
||||||
when compileOption("rangechecks"):
|
when compileOption("rangechecks"):
|
||||||
if i > a.len:
|
if p +% sizeof(T) > cast[int](a.endp): rangeError(a, i)
|
||||||
raise newException(RangeDefect, &"index out of range: {i} >= {a.len}")
|
cast[ptr T](p)[]
|
||||||
a.arr[i]
|
|
||||||
|
|
||||||
proc `[]=`*[T](a: ArrRef[T], i: Natural, v: T) =
|
proc `[]=`*[T](a: ArrRef[T], i: Natural, v: T) =
|
||||||
|
let p = cast[int](a) +% header_size +% sizeof(T) * i
|
||||||
when compileOption("rangechecks"):
|
when compileOption("rangechecks"):
|
||||||
if i > a.len:
|
if p +% sizeof(T) > cast[int](a.endp): rangeError(a, i)
|
||||||
raise newException(RangeDefect, &"index out of range: {i} >= {a.len}")
|
cast[ptr T](p)[] = v
|
||||||
a.arr[i] = v
|
|
||||||
|
|
||||||
template toPointer*[T](a: ArrRef[T]): pointer = a.arr[0].addr
|
template toPointer*[T](a: ArrRef[T]): pointer = a.arr[0].addr
|
||||||
|
|
||||||
|
@ -67,13 +70,11 @@ iterator mpairs*[T](a: ArrRef[T]): tuple[key: int, val: var T] =
|
||||||
yield (i, a[i])
|
yield (i, a[i])
|
||||||
|
|
||||||
proc `$`*[T](a: ArrRef[T]): string =
|
proc `$`*[T](a: ArrRef[T]): string =
|
||||||
result = "ArrRef(["
|
result = "["
|
||||||
if a.endp != a.arr:
|
let hi = a.high
|
||||||
let hi = a.high
|
for i in 0 ..< hi:
|
||||||
for i in 0 ..< hi:
|
result &= $a[i] & ", "
|
||||||
result &= $a[i] & ", "
|
result &= $a[hi] & "]"
|
||||||
result &= $a[hi]
|
|
||||||
result &= "])"
|
|
||||||
|
|
||||||
template to*[T](a: ArrRef[T], U: untyped): untyped =
|
template to*[T](a: ArrRef[T], U: untyped): untyped =
|
||||||
cast[ArrRef[U]](a)
|
cast[ArrRef[U]](a)
|
||||||
|
@ -103,8 +104,9 @@ proc newArrRefWith*[T](size: Natural, v: T): ArrRef[T] =
|
||||||
result = cast[ArrRef[T]](r)
|
result = cast[ArrRef[T]](r)
|
||||||
else:
|
else:
|
||||||
unsafeNew(result, size * sizeof(T) + header_size)
|
unsafeNew(result, size * sizeof(T) + header_size)
|
||||||
result.byte_len = size * sizeof(T)
|
result.endp = addr(result.arr[size])
|
||||||
when not defined(release):
|
when not defined(release):
|
||||||
|
result.size_bytes = size * sizeof(T)
|
||||||
result.arr_ptr = result.arr[0].addr
|
result.arr_ptr = result.arr[0].addr
|
||||||
for i in result.low .. result.high: result[i] = v
|
for i in result.low .. result.high: result[i] = v
|
||||||
|
|
||||||
|
@ -135,7 +137,3 @@ proc hash*[T](arr: ArrRef[T]): Hash =
|
||||||
# just in case the actual size is bigger than that.
|
# just in case the actual size is bigger than that.
|
||||||
hash(cast[ptr UncheckedArray[byte]](arr.arr.addr).toOpenArray(0, arr.len * sizeof(T) - 1))
|
hash(cast[ptr UncheckedArray[byte]](arr.arr.addr).toOpenArray(0, arr.len * sizeof(T) - 1))
|
||||||
# TODO: for bigger elements, would a different algorithm be faster?
|
# TODO: for bigger elements, would a different algorithm be faster?
|
||||||
|
|
||||||
template `[]`*[T](a: ArrRef[T], s: Slice[int]): var SliceMem[T] =
|
|
||||||
toSliceMem(a, s)
|
|
||||||
|
|
||||||
|
|
|
@ -1,148 +0,0 @@
|
||||||
|
|
||||||
## SliceMem represents a slice of memory, but it includes a reference to the
|
|
||||||
## original container (of any type) or even a custom destructor, so memory is
|
|
||||||
## properly freed when it's no longer in use.
|
|
||||||
##
|
|
||||||
## It's called "SliceMem" because "MemSlice" is taken by std/memfiles.
|
|
||||||
|
|
||||||
import std/strformat
|
|
||||||
import std/hashes
|
|
||||||
import std/macros
|
|
||||||
|
|
||||||
type
|
|
||||||
SliceMem*[T] = object
|
|
||||||
data*: ptr UncheckedArray[T]
|
|
||||||
byte_len*: int
|
|
||||||
destroy_ref: ref CustomDestructor
|
|
||||||
|
|
||||||
CustomDestructor = object
|
|
||||||
destroy: proc() {.closure, raises: [].}
|
|
||||||
|
|
||||||
proc `=destroy`(s: var CustomDestructor) =
|
|
||||||
if s.destroy != nil:
|
|
||||||
s.destroy()
|
|
||||||
s.destroy = nil
|
|
||||||
|
|
||||||
template toInt(p: pointer): int = cast[int](p)
|
|
||||||
# template toPointer(i: int): pointer = cast[pointer](p)
|
|
||||||
|
|
||||||
proc newSliceMem*[T, U](container: sink U; p: pointer, byte_len: int): SliceMem[T] =
|
|
||||||
## Create a SliceMem from a container, a pointer, and a length in bytes.
|
|
||||||
result = SliceMem[T](
|
|
||||||
data: cast[ptr UncheckedArray[T]](p),
|
|
||||||
byte_len: byte_len,
|
|
||||||
destroy_ref: (ref CustomDestructor)(destroy: proc()=
|
|
||||||
# we don't actually need to destroy the container here, destroying
|
|
||||||
# the closure will do it for us. Calling it allows us to use custom
|
|
||||||
# destructors though.
|
|
||||||
discard container
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
proc newSliceMem*[T](p: ptr T, byte_len: int, destructor: proc() {.closure, raises: [].}): SliceMem[T] =
|
|
||||||
## Create a SliceMem from a pointer to a type, a length in bytes, and a
|
|
||||||
## destructor closure.
|
|
||||||
result = SliceMem[T](
|
|
||||||
data: cast[ptr UncheckedArray[T]](p),
|
|
||||||
byte_len: byte_len,
|
|
||||||
destroy_ref: (ref CustomDestructor)(destroy: destructor),
|
|
||||||
)
|
|
||||||
|
|
||||||
proc newSliceMem*(p: pointer, byte_len: int, destructor: proc() {.closure, raises: [].}): SliceMem[byte] {.inline.} =
|
|
||||||
newSliceMem(cast[ptr byte](p), byte_len, destructor)
|
|
||||||
|
|
||||||
template newSliceMem*(container: not pointer, p, byte_len: untyped): untyped =
|
|
||||||
## Template to automatically determine the type for `newSliceMem[T,U]`
|
|
||||||
newSliceMem[typeof(container.items)](container, p, byte_len)
|
|
||||||
|
|
||||||
proc newSliceMem*[T, U](container: sink U; first, last: pointer): SliceMem[T] =
|
|
||||||
## Create a SliceMem from a container and the two pointers of the first and
|
|
||||||
## last elements in the container. Usually you will want to use `toSliceMem`
|
|
||||||
## instead.
|
|
||||||
result = SliceMem[T](
|
|
||||||
data: cast[ptr UncheckedArray[T]](first),
|
|
||||||
byte_len: (last.toInt -% first.toInt) + sizeof(T),
|
|
||||||
destroy_ref: (ref CustomDestructor)(destroy: proc()=
|
|
||||||
discard container
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
macro noMove(e: untyped): untyped =
|
|
||||||
if e.kind == nnkCommand and e[0].repr == "move": e[1]
|
|
||||||
else: e
|
|
||||||
|
|
||||||
template toSliceMem*(container, slice: untyped): untyped =
|
|
||||||
## Create a SliceMem from a container and a slice that indicates the range.
|
|
||||||
## The container needs to have `[]` and `items()` (like `seq`). If it doesn't
|
|
||||||
## you need to pass pointers directly to `newSliceMem` instead.
|
|
||||||
if slice.len != 0:
|
|
||||||
# TODO: check that with boundChecks:off it doesn't try to read the value
|
|
||||||
let first = noMove(container)[slice.a].addr
|
|
||||||
let last = noMove(container)[slice.b].addr
|
|
||||||
newSliceMem[typeof(noMove(container).items)](container, first, last)
|
|
||||||
else:
|
|
||||||
SliceMem[typeof(container.items)]()
|
|
||||||
|
|
||||||
template toSliceMem*(container): untyped =
|
|
||||||
## Create a SliceMem from a container, with all its contents. The container
|
|
||||||
## needs to have `[]` and `items()` (like `seq`). If it doesn't you need to
|
|
||||||
## pass pointers directly to `newSliceMem` instead.
|
|
||||||
let s = noMove(container).low .. noMove(container).high
|
|
||||||
toSliceMem(container, s)
|
|
||||||
|
|
||||||
template len*[T](s: SliceMem[T]): Natural =
|
|
||||||
s.byte_len div sizeof(T)
|
|
||||||
|
|
||||||
template low*[T](s: SliceMem[T]): Natural = 0
|
|
||||||
|
|
||||||
template high*[T](s: SliceMem[T]): int = s.len - 1
|
|
||||||
|
|
||||||
template `[]`*[T](s: SliceMem[T], i: Natural): var T =
|
|
||||||
when compileOption("rangechecks"):
|
|
||||||
if i >= s.len:
|
|
||||||
raise newException(RangeDefect, &"index out of range: {i} >= {s.len}")
|
|
||||||
s.data[i]
|
|
||||||
|
|
||||||
proc `[]=`*[T](s: SliceMem[T], i: Natural, v: T) =
|
|
||||||
when compileOption("rangechecks"):
|
|
||||||
if i >= s.len:
|
|
||||||
raise newException(RangeDefect, &"index out of range: {i} >= {s.len}")
|
|
||||||
s.data[i] = v
|
|
||||||
|
|
||||||
iterator items*[T](s: SliceMem[T]): T =
|
|
||||||
for i in 0 ..< s.len:
|
|
||||||
yield s.data[i]
|
|
||||||
|
|
||||||
iterator pairs*[T](s: SliceMem[T]): tuple[key: int, val: T] =
|
|
||||||
for i in 0 ..< s.len:
|
|
||||||
yield (i, s.data[i])
|
|
||||||
|
|
||||||
iterator mitems*[T](s: SliceMem[T]): var T =
|
|
||||||
for i in 0 ..< s.len:
|
|
||||||
yield s.data[i]
|
|
||||||
|
|
||||||
iterator mpairs*[T](s: SliceMem[T]): tuple[key: int, val: var T] =
|
|
||||||
for i in 0 ..< s.len:
|
|
||||||
yield (i, s.data[i])
|
|
||||||
|
|
||||||
proc `$`*[T](s: SliceMem[T]): string =
|
|
||||||
result = "SliceMem(["
|
|
||||||
if s.byte_len != 0:
|
|
||||||
let hi = s.high
|
|
||||||
for i in 0 ..< hi:
|
|
||||||
result &= $s.data[i] & ", "
|
|
||||||
result &= $s.data[hi]
|
|
||||||
result &= "])"
|
|
||||||
|
|
||||||
proc hash*[T](s: SliceMem[T]): Hash =
|
|
||||||
# Make use of stdlib's murmur3
|
|
||||||
hash(cast[ptr UncheckedArray[byte]](s.data).toOpenArray(0, s.byte_len - 1))
|
|
||||||
|
|
||||||
template toOpenArray*(s: SliceMem): untyped =
|
|
||||||
s.data.toOpenArray(0, s.byte_len - 1)
|
|
||||||
|
|
||||||
template toOpenArrayByte*(s: SliceMem): untyped =
|
|
||||||
cast[ptr UncheckedArray[byte]](s.data).toOpenArray(0, s.byte_len - 1)
|
|
||||||
|
|
||||||
template to*[T](s: SliceMem, typ: typedesc[T]): SliceMem[T] =
|
|
||||||
cast[SliceMem[T]](s)
|
|
|
@ -1,200 +0,0 @@
|
||||||
# The contents of this file are subject to the Common Public Attribution License
|
|
||||||
# Version 1.0 (the “License”); you may not use this file except in compliance
|
|
||||||
# with the License. You may obtain a copy of the License at
|
|
||||||
# https://myou.dev/licenses/LICENSE-CPAL. The License is based on the Mozilla
|
|
||||||
# Public License Version 1.1 but Sections 14 and 15 have been added to cover use
|
|
||||||
# of software over a computer network and provide for limited attribution for
|
|
||||||
# the Original Developer. In addition, Exhibit A has been modified to be
|
|
||||||
# consistent with Exhibit B.
|
|
||||||
#
|
|
||||||
# Software distributed under the License is distributed on an “AS IS” basis,
|
|
||||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
|
||||||
# the specific language governing rights and limitations under the License.
|
|
||||||
#
|
|
||||||
# The Original Code is Myou Engine.
|
|
||||||
#
|
|
||||||
# the Original Developer is the Initial Developer.
|
|
||||||
#
|
|
||||||
# The Initial Developer of the Original Code is the Myou Engine developers.
|
|
||||||
# All portions of the code written by the Myou Engine developers are Copyright
|
|
||||||
# (c) 2024. All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Alternatively, the contents of this file may be used under the terms of the
|
|
||||||
# GNU Affero General Public License version 3 (the [AGPL-3] License), in which
|
|
||||||
# case the provisions of [AGPL-3] License are applicable instead of those above.
|
|
||||||
#
|
|
||||||
# If you wish to allow use of your version of this file only under the terms of
|
|
||||||
# the [AGPL-3] License and not to allow others to use your version of this file
|
|
||||||
# under the CPAL, indicate your decision by deleting the provisions above and
|
|
||||||
# replace them with the notice and other provisions required by the [AGPL-3]
|
|
||||||
# License. If you do not delete the provisions above, a recipient may use your
|
|
||||||
# version of this file under either the CPAL or the [AGPL-3] License.
|
|
||||||
|
|
||||||
import ../types
|
|
||||||
|
|
||||||
# Forward declarations
|
|
||||||
proc swap_lines(p: pointer, line_stride, line_count: int)
|
|
||||||
# End forward declarations
|
|
||||||
|
|
||||||
import std/strformat
|
|
||||||
import vmath except Quat
|
|
||||||
import arr_ref
|
|
||||||
import float16
|
|
||||||
|
|
||||||
when defined(myouUsePixie):
|
|
||||||
import pixie
|
|
||||||
else:
|
|
||||||
import stb_image/read as stbi
|
|
||||||
const myouConvertHdrToFloat16 {.booldefine.} = true
|
|
||||||
|
|
||||||
when not defined(nimdoc):
|
|
||||||
import tinyexr
|
|
||||||
|
|
||||||
proc stride*(format: TextureFormat): int =
|
|
||||||
case format:
|
|
||||||
of SRGB_u8: 3
|
|
||||||
of SRGB_Alpha_u8: 4
|
|
||||||
of R_u8: 1
|
|
||||||
of RG_u8: 2
|
|
||||||
of RGB_u8: 3
|
|
||||||
of RGBA_u8: 4
|
|
||||||
of R_u16, R_f16: 2
|
|
||||||
of RG_u16, RG_f16: 4
|
|
||||||
of RGB_u16, RGB_f16: 6
|
|
||||||
of RGBA_u16, RGBA_f16: 8
|
|
||||||
of R_f32: 4
|
|
||||||
of RG_f32: 8
|
|
||||||
of RGB_f32: 12
|
|
||||||
of RGBA_f32: 16
|
|
||||||
of Depth_u16: 2
|
|
||||||
of Depth_u24: 3 # TODO: are you sure?
|
|
||||||
# of Depth_u24_s8: 4
|
|
||||||
of Depth_f32: 4
|
|
||||||
|
|
||||||
proc channel_count*(format: TextureFormat): int =
|
|
||||||
case format:
|
|
||||||
of R_u8, R_u16, R_f16, R_f32, Depth_u16, Depth_u24, Depth_f32: 1
|
|
||||||
of RG_u8, RG_u16, RG_f16, RG_f32: 2
|
|
||||||
of SRGB_u8, RGB_u8, RGB_u16, RGB_f16, RGB_f32: 3
|
|
||||||
else: 4
|
|
||||||
|
|
||||||
template toOpenArrayByte(p: pointer, a,b: untyped): untyped =
|
|
||||||
cast[ptr UncheckedArray[byte]](p).toOpenArray(a,b)
|
|
||||||
|
|
||||||
# template toOpenArray[T](p: pointer, a,b: untyped): untyped =
|
|
||||||
# cast[ptr UncheckedArray[T]](p).toOpenArray(a,b)
|
|
||||||
|
|
||||||
proc f32_to_f16(source: ptr UncheckedArray[float32], dest: ptr UncheckedArray[Float16], len: int) =
|
|
||||||
for i in 0 ..< len:
|
|
||||||
dest[i] = source[i].tofloat16(clamp=true)
|
|
||||||
|
|
||||||
proc getDimensionsFormat*(p: pointer, len: int): (int, int, TextureFormat) =
|
|
||||||
when not defined(nimdoc):
|
|
||||||
if isEXR(p, len):
|
|
||||||
let dims = getEXRDimensions(p, len)
|
|
||||||
return (dims[0], dims[1], RGBA_f16)
|
|
||||||
when defined(myouUsePixie):
|
|
||||||
let dims = decodeImageDimensions(p, len)
|
|
||||||
return (dims.width, dims.height, RGBA_u8)
|
|
||||||
else:
|
|
||||||
var width, height, channels = 0
|
|
||||||
if not infoFromMemory(p.toOpenArrayByte(0, len-1), width, height, channels):
|
|
||||||
raise ValueError.newException "Could not read image"
|
|
||||||
let hdr = isHDRFromMemory(p.toOpenArrayByte(0, len-1))
|
|
||||||
let is16 = is16BitFromMemory(p.toOpenArrayByte(0, len-1))
|
|
||||||
# Calculate format with channels, and whether it's hdr or 16 bit
|
|
||||||
assert (RG_u8.int - R_u8.int) == 1 # (just in case someone changes the enum)
|
|
||||||
const toHDR = when myouConvertHdrToFloat16: (R_f16.int-R_u8.int) else: (R_f32.int-R_u8.int)
|
|
||||||
let format = (R_u8.int - 1 + channels +
|
|
||||||
hdr.int * toHDR + is16.int * (R_u16.int-R_u8.int)).TextureFormat
|
|
||||||
return (width, height, format)
|
|
||||||
|
|
||||||
proc loadFileFromPointersLen*(tex: Texture, pointers: seq[(pointer, int)],
|
|
||||||
callback: proc(tex: Texture, p: pointer), flip = true) =
|
|
||||||
|
|
||||||
when not defined(nimdoc):
|
|
||||||
assert tex.tex_type != TexCube, "Loading a cube texture from file is not supported yet"
|
|
||||||
let layer_stride = tex.width * tex.height * tex.format.stride
|
|
||||||
var multilayer_buffer: ArrRef[byte]
|
|
||||||
assert tex.depth == pointers.len
|
|
||||||
if tex.depth > 1:
|
|
||||||
multilayer_buffer = newArrRef[byte](layer_stride * tex.depth)
|
|
||||||
var pos = 0
|
|
||||||
for (p, len) in pointers:
|
|
||||||
when defined(myouUsePixie):
|
|
||||||
var image: Image
|
|
||||||
else:
|
|
||||||
var image: imagePixelData[byte]
|
|
||||||
var image_16: imagePixelData[uint16]
|
|
||||||
var image_f: imagePixelData[float32]
|
|
||||||
var buffer: ArrRef[byte]
|
|
||||||
# a reference to this pointer is kept with one of the vars above
|
|
||||||
var pixels_ptr: pointer
|
|
||||||
var pixels_len: int
|
|
||||||
var flip = flip
|
|
||||||
if isEXR(p, len):
|
|
||||||
let (width, height, pixels) = decodeEXR(p, len)
|
|
||||||
assert width == tex.width and height == tex.height, "Image size mismatch"
|
|
||||||
buffer = pixels.to byte
|
|
||||||
pixels_ptr = buffer[0].addr
|
|
||||||
pixels_len = buffer.len
|
|
||||||
else:
|
|
||||||
when defined(myouUsePixie):
|
|
||||||
image = decodeImage(p, len)
|
|
||||||
assert image.width == tex.width and image.height == tex.height, "Image size mismatch"
|
|
||||||
pixels_ptr = image.data[0].addr
|
|
||||||
pixels_len = image.data.len * sizeof image.data[0]
|
|
||||||
else:
|
|
||||||
setFlipVerticallyOnLoad(flip)
|
|
||||||
flip = false
|
|
||||||
var w,h,c = 0
|
|
||||||
if isHDRFromMemory(p.toOpenArrayByte(0, len-1)):
|
|
||||||
image_f = loadFFromMemory(p.toOpenArrayByte(0, len-1), w,h,c,0)
|
|
||||||
pixels_ptr = image_f.data
|
|
||||||
pixels_len = image_f.byteLen
|
|
||||||
when myouConvertHdrToFloat16:
|
|
||||||
f32_to_f16(
|
|
||||||
cast[ptr UncheckedArray[float32]](pixels_ptr),
|
|
||||||
cast[ptr UncheckedArray[Float16]](pixels_ptr),
|
|
||||||
image_f.len)
|
|
||||||
pixels_len = pixels_len div 2
|
|
||||||
elif is16BitFromMemory(p.toOpenArrayByte(0, len-1)):
|
|
||||||
image_16 = load16FromMemory(p.toOpenArrayByte(0, len-1), w,h,c,0)
|
|
||||||
pixels_ptr = image_16.data
|
|
||||||
pixels_len = image_16.byteLen
|
|
||||||
else:
|
|
||||||
image = loadFromMemory(p.toOpenArrayByte(0, len-1), w,h,c,0)
|
|
||||||
pixels_ptr = image.data
|
|
||||||
pixels_len = image.len
|
|
||||||
assert layer_stride == pixels_len,
|
|
||||||
&"Image '{tex.name}' has a length of {pixels_len}, expected {layer_stride}"
|
|
||||||
if flip:
|
|
||||||
swap_lines(pixels_ptr, tex.width * tex.format.stride, tex.height)
|
|
||||||
if tex.depth == 1:
|
|
||||||
callback(tex, pixels_ptr)
|
|
||||||
return
|
|
||||||
copyMem(multilayer_buffer[pos].addr, pixels_ptr, layer_stride)
|
|
||||||
pos += layer_stride
|
|
||||||
callback(tex, multilayer_buffer[0].addr)
|
|
||||||
|
|
||||||
|
|
||||||
proc swap_lines(p: pointer, line_stride, line_count: int) =
|
|
||||||
template `+`(p: pointer, i: Natural): pointer = cast[pointer](cast[int](p) +% cast[int](i))
|
|
||||||
template `-`(p: pointer, i: Natural): pointer = cast[pointer](cast[int](p) -% cast[int](i))
|
|
||||||
let int_stride = line_stride div sizeof(int)
|
|
||||||
let int_stride_bytes = int_stride * sizeof(int)
|
|
||||||
var p1 = p
|
|
||||||
var p2 = p + line_stride*(line_count-1)
|
|
||||||
var a1, a2: ptr UncheckedArray[int]
|
|
||||||
var b1, b2: ptr UncheckedArray[byte]
|
|
||||||
for i in 0 ..< line_count div 2:
|
|
||||||
a1 = cast[ptr UncheckedArray[int]](p1)
|
|
||||||
a2 = cast[ptr UncheckedArray[int]](p2)
|
|
||||||
b1 = cast[ptr UncheckedArray[byte]](p1)
|
|
||||||
b2 = cast[ptr UncheckedArray[byte]](p2)
|
|
||||||
for j in 0 ..< int_stride:
|
|
||||||
swap(a1[j], a2[j])
|
|
||||||
for j in int_stride_bytes ..< line_stride:
|
|
||||||
swap(b1[j], b2[j])
|
|
||||||
p1 = p1 + line_stride
|
|
||||||
p2 = p2 - line_stride
|
|
|
@ -35,10 +35,11 @@ import vmath except Quat
|
||||||
import arr_ref
|
import arr_ref
|
||||||
# import tinyre
|
# import tinyre
|
||||||
import std/tables
|
import std/tables
|
||||||
import ../gpu_formats/texture_decode
|
|
||||||
import ../platform/gl
|
import ../platform/gl
|
||||||
|
|
||||||
# Forward declarations
|
# Forward declarations
|
||||||
|
proc stride*(format: TextureFormat): int
|
||||||
|
proc channel_count*(format: TextureFormat): int
|
||||||
func mipmapHigh*(self: Texture): int
|
func mipmapHigh*(self: Texture): int
|
||||||
proc needsMipmap*(self: Texture): bool
|
proc needsMipmap*(self: Texture): bool
|
||||||
proc setMaxTextures*(count: int32)
|
proc setMaxTextures*(count: int32)
|
||||||
|
@ -51,6 +52,7 @@ proc unbindAllTextures*()
|
||||||
proc destroy*(texture: Texture)
|
proc destroy*(texture: Texture)
|
||||||
proc loadFromPixels*(self: Texture, pixels: pointer)
|
proc loadFromPixels*(self: Texture, pixels: pointer)
|
||||||
proc loadCubeSideFromPixels*(self: Texture, pixels: pointer, side: int32 = 0)
|
proc loadCubeSideFromPixels*(self: Texture, pixels: pointer, side: int32 = 0)
|
||||||
|
proc loadFileFromPointersLen*(self: Texture, pointers: seq[(pointer, int)], flip = true)
|
||||||
proc setFilter*(self: Texture, filter: TextureFilter)
|
proc setFilter*(self: Texture, filter: TextureFilter)
|
||||||
proc newTexture*(engine: MyouEngine, name: string, width, height: int, depth: int = 1,
|
proc newTexture*(engine: MyouEngine, name: string, width, height: int, depth: int = 1,
|
||||||
format: TextureFormat,
|
format: TextureFormat,
|
||||||
|
@ -58,6 +60,7 @@ proc newTexture*(engine: MyouEngine, name: string, width, height: int, depth: in
|
||||||
filter: TextureFilter = Trilinear,
|
filter: TextureFilter = Trilinear,
|
||||||
pixels: ArrRef[float32] = nil): Texture
|
pixels: ArrRef[float32] = nil): Texture
|
||||||
proc generateMipmap*(self: Texture)
|
proc generateMipmap*(self: Texture)
|
||||||
|
proc getDimensionsFormat(p: pointer, len: int): (int, int, TextureFormat)
|
||||||
func to_sRGB*(format: TextureFormat): TextureFormat
|
func to_sRGB*(format: TextureFormat): TextureFormat
|
||||||
proc newTexture*(engine: MyouEngine, name: string, p: pointer, len: int, is_sRGB: bool, filter: TextureFilter = Trilinear, depth=1, flip=true): Texture
|
proc newTexture*(engine: MyouEngine, name: string, p: pointer, len: int, is_sRGB: bool, filter: TextureFilter = Trilinear, depth=1, flip=true): Texture
|
||||||
proc newTexture*(engine: MyouEngine, name: string, file_name: string, is_sRGB: bool,
|
proc newTexture*(engine: MyouEngine, name: string, file_name: string, is_sRGB: bool,
|
||||||
|
@ -66,6 +69,7 @@ proc newTexture*(engine: MyouEngine, name: string, file_name: string, is_sRGB: b
|
||||||
flip = true,
|
flip = true,
|
||||||
): Texture
|
): Texture
|
||||||
proc setExtrapolation*(self: Texture, ext: TextureExtrapolation)
|
proc setExtrapolation*(self: Texture, ext: TextureExtrapolation)
|
||||||
|
proc swap_lines(p: pointer, line_stride, line_count: int)
|
||||||
proc getTexturePixels*(self: Texture): TexturePixels
|
proc getTexturePixels*(self: Texture): TexturePixels
|
||||||
proc setMipmapRange*(self: Texture, first = 0, last = 1000)
|
proc setMipmapRange*(self: Texture, first = 0, last = 1000)
|
||||||
func vec3size*(self: Texture, mip_level = -1): Vec3
|
func vec3size*(self: Texture, mip_level = -1): Vec3
|
||||||
|
@ -74,8 +78,18 @@ func toInternalFormat*(format: TextureFormat): GLenum
|
||||||
|
|
||||||
# import sugar
|
# import sugar
|
||||||
|
|
||||||
|
when defined(myouUsePixie):
|
||||||
|
import pixie
|
||||||
|
else:
|
||||||
|
import stb_image/read as stbi
|
||||||
|
const myouConvertHdrToFloat16 {.booldefine.} = true
|
||||||
|
|
||||||
|
when not defined(nimdoc):
|
||||||
|
import tinyexr
|
||||||
|
|
||||||
import std/bitops
|
import std/bitops
|
||||||
import std/strformat
|
import std/strformat
|
||||||
|
import float16
|
||||||
import loadable
|
import loadable
|
||||||
import ddx_ktx
|
import ddx_ktx
|
||||||
|
|
||||||
|
@ -96,6 +110,34 @@ if defined(android):
|
||||||
# even if it's not used, we should test it
|
# even if it's not used, we should test it
|
||||||
reserved = 1
|
reserved = 1
|
||||||
|
|
||||||
|
proc stride*(format: TextureFormat): int =
|
||||||
|
case format:
|
||||||
|
of SRGB_u8: 3
|
||||||
|
of SRGB_Alpha_u8: 4
|
||||||
|
of R_u8: 1
|
||||||
|
of RG_u8: 2
|
||||||
|
of RGB_u8: 3
|
||||||
|
of RGBA_u8: 4
|
||||||
|
of R_u16, R_f16: 2
|
||||||
|
of RG_u16, RG_f16: 4
|
||||||
|
of RGB_u16, RGB_f16: 6
|
||||||
|
of RGBA_u16, RGBA_f16: 8
|
||||||
|
of R_f32: 4
|
||||||
|
of RG_f32: 8
|
||||||
|
of RGB_f32: 12
|
||||||
|
of RGBA_f32: 16
|
||||||
|
of Depth_u16: 2
|
||||||
|
of Depth_u24: 3 # TODO: are you sure?
|
||||||
|
# of Depth_u24_s8: 4
|
||||||
|
of Depth_f32: 4
|
||||||
|
|
||||||
|
proc channel_count*(format: TextureFormat): int =
|
||||||
|
case format:
|
||||||
|
of R_u8, R_u16, R_f16, R_f32, Depth_u16, Depth_u24, Depth_f32: 1
|
||||||
|
of RG_u8, RG_u16, RG_f16, RG_f32: 2
|
||||||
|
of SRGB_u8, RGB_u8, RGB_u16, RGB_f16, RGB_f32: 3
|
||||||
|
else: 4
|
||||||
|
|
||||||
func samplerType*(tex_type: TextureType): string =
|
func samplerType*(tex_type: TextureType): string =
|
||||||
case tex_type:
|
case tex_type:
|
||||||
of Tex2D: "sampler2D"
|
of Tex2D: "sampler2D"
|
||||||
|
@ -367,6 +409,82 @@ proc loadCompressedData*(self: Texture, info: KtxInfo, data: seq[KtxPart]) =
|
||||||
info.internal_format.GLenum, self.width.GLsizei, self.height.GLsizei,
|
info.internal_format.GLenum, self.width.GLsizei, self.height.GLsizei,
|
||||||
0, part.len.GLsizei, part.data)
|
0, part.len.GLsizei, part.data)
|
||||||
|
|
||||||
|
template toOpenArrayByte(p: pointer, a,b: untyped): untyped =
|
||||||
|
cast[ptr UncheckedArray[byte]](p).toOpenArray(a,b)
|
||||||
|
|
||||||
|
template toOpenArray[T](p: pointer, a,b: untyped): untyped =
|
||||||
|
cast[ptr UncheckedArray[T]](p).toOpenArray(a,b)
|
||||||
|
|
||||||
|
proc f32_to_f16(source: ptr UncheckedArray[float32], dest: ptr UncheckedArray[Float16], len: int) =
|
||||||
|
for i in 0 ..< len:
|
||||||
|
dest[i] = source[i].tofloat16(clamp=true)
|
||||||
|
|
||||||
|
proc loadFileFromPointersLen*(self: Texture, pointers: seq[(pointer, int)], flip = true) =
|
||||||
|
when not defined(nimdoc):
|
||||||
|
assert self.tex_type != TexCube, "Loading a cube texture from file is not supported yet"
|
||||||
|
let layer_stride = self.width * self.height * self.format.stride
|
||||||
|
var multilayer_buffer: ArrRef[byte]
|
||||||
|
assert self.depth == pointers.len
|
||||||
|
if self.depth > 1:
|
||||||
|
multilayer_buffer = newArrRef[byte](layer_stride * self.depth)
|
||||||
|
var pos = 0
|
||||||
|
for (p, len) in pointers:
|
||||||
|
when defined(myouUsePixie):
|
||||||
|
var image: Image
|
||||||
|
else:
|
||||||
|
var image: imagePixelData[byte]
|
||||||
|
var image_16: imagePixelData[uint16]
|
||||||
|
var image_f: imagePixelData[float32]
|
||||||
|
var buffer: ArrRef[byte]
|
||||||
|
# a reference to this pointer is kept with one of the vars above
|
||||||
|
var pixels_ptr: pointer
|
||||||
|
var pixels_len: int
|
||||||
|
var flip = flip
|
||||||
|
if isEXR(p, len):
|
||||||
|
let (width, height, pixels) = decodeEXR(p, len)
|
||||||
|
assert width == self.width and height == self.height, "Image size mismatch"
|
||||||
|
buffer = pixels.to byte
|
||||||
|
pixels_ptr = buffer[0].addr
|
||||||
|
pixels_len = buffer.len
|
||||||
|
else:
|
||||||
|
when defined(myouUsePixie):
|
||||||
|
image = decodeImage(p, len)
|
||||||
|
assert image.width == self.width and image.height == self.height, "Image size mismatch"
|
||||||
|
pixels_ptr = image.data[0].addr
|
||||||
|
pixels_len = image.data.len * sizeof image.data[0]
|
||||||
|
else:
|
||||||
|
setFlipVerticallyOnLoad(flip)
|
||||||
|
flip = false
|
||||||
|
var w,h,c = 0
|
||||||
|
if isHDRFromMemory(p.toOpenArrayByte(0, len-1)):
|
||||||
|
image_f = loadFFromMemory(p.toOpenArrayByte(0, len-1), w,h,c,0)
|
||||||
|
pixels_ptr = image_f.data
|
||||||
|
pixels_len = image_f.byteLen
|
||||||
|
when myouConvertHdrToFloat16:
|
||||||
|
f32_to_f16(
|
||||||
|
cast[ptr UncheckedArray[float32]](pixels_ptr),
|
||||||
|
cast[ptr UncheckedArray[Float16]](pixels_ptr),
|
||||||
|
image_f.len)
|
||||||
|
pixels_len = pixels_len div 2
|
||||||
|
elif is16BitFromMemory(p.toOpenArrayByte(0, len-1)):
|
||||||
|
image_16 = load16FromMemory(p.toOpenArrayByte(0, len-1), w,h,c,0)
|
||||||
|
pixels_ptr = image_16.data
|
||||||
|
pixels_len = image_16.byteLen
|
||||||
|
else:
|
||||||
|
image = loadFromMemory(p.toOpenArrayByte(0, len-1), w,h,c,0)
|
||||||
|
pixels_ptr = image.data
|
||||||
|
pixels_len = image.len
|
||||||
|
assert layer_stride == pixels_len,
|
||||||
|
&"Image '{self.name}' has a length of {pixels_len}, expected {layer_stride}"
|
||||||
|
if flip:
|
||||||
|
swap_lines(pixels_ptr, self.width * self.format.stride, self.height)
|
||||||
|
if self.depth == 1:
|
||||||
|
self.loadFromPixels pixels_ptr
|
||||||
|
return
|
||||||
|
copyMem(multilayer_buffer[pos].addr, pixels_ptr, layer_stride)
|
||||||
|
pos += layer_stride
|
||||||
|
self.loadFromPixels multilayer_buffer[0].addr
|
||||||
|
|
||||||
proc setFilter*(self: Texture, filter: TextureFilter) =
|
proc setFilter*(self: Texture, filter: TextureFilter) =
|
||||||
self.filter = filter
|
self.filter = filter
|
||||||
self.engine.renderer.enqueue proc()=
|
self.engine.renderer.enqueue proc()=
|
||||||
|
@ -430,6 +548,27 @@ proc generateMipmap*(self: Texture) =
|
||||||
self.bind_it(needs_active_texture=true)
|
self.bind_it(needs_active_texture=true)
|
||||||
glGenerateMipmap(self.storage.target)
|
glGenerateMipmap(self.storage.target)
|
||||||
|
|
||||||
|
proc getDimensionsFormat(p: pointer, len: int): (int, int, TextureFormat) =
|
||||||
|
when not defined(nimdoc):
|
||||||
|
if isEXR(p, len):
|
||||||
|
let dims = getEXRDimensions(p, len)
|
||||||
|
return (dims[0], dims[1], RGBA_f16)
|
||||||
|
when defined(myouUsePixie):
|
||||||
|
let dims = decodeImageDimensions(p, len)
|
||||||
|
return (dims.width, dims.height, RGBA_u8)
|
||||||
|
else:
|
||||||
|
var width, height, channels = 0
|
||||||
|
if not infoFromMemory(p.toOpenArrayByte(0, len-1), width, height, channels):
|
||||||
|
raise ValueError.newException "Could not read image"
|
||||||
|
let hdr = isHDRFromMemory(p.toOpenArrayByte(0, len-1))
|
||||||
|
let is16 = is16BitFromMemory(p.toOpenArrayByte(0, len-1))
|
||||||
|
# Calculate format with channels, and whether it's hdr or 16 bit
|
||||||
|
assert (RG_u8.int - R_u8.int) == 1 # (just in case someone changes the enum)
|
||||||
|
const toHDR = when myouConvertHdrToFloat16: (R_f16.int-R_u8.int) else: (R_f32.int-R_u8.int)
|
||||||
|
let format = (R_u8.int - 1 + channels +
|
||||||
|
hdr.int * toHDR + is16.int * (R_u16.int-R_u8.int)).TextureFormat
|
||||||
|
return (width, height, format)
|
||||||
|
|
||||||
func to_sRGB*(format: TextureFormat): TextureFormat =
|
func to_sRGB*(format: TextureFormat): TextureFormat =
|
||||||
return case format:
|
return case format:
|
||||||
of RGBA_u8: SRGB_Alpha_u8
|
of RGBA_u8: SRGB_Alpha_u8
|
||||||
|
@ -443,7 +582,7 @@ proc newTexture*(engine: MyouEngine, name: string, p: pointer, len: int, is_sRGB
|
||||||
format = format.to_sRGB
|
format = format.to_sRGB
|
||||||
let self = engine.newTexture(name, width, height, depth, format, filter=filter)
|
let self = engine.newTexture(name, width, height, depth, format, filter=filter)
|
||||||
engine.renderer.enqueue proc() =
|
engine.renderer.enqueue proc() =
|
||||||
self.loadFileFromPointersLen(@[(p, len)], loadFromPixels, flip=flip)
|
self.loadFileFromPointersLen(@[(p, len)], flip=flip)
|
||||||
self.loaded = true
|
self.loaded = true
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@ -494,7 +633,7 @@ proc newTexture*(engine: MyouEngine, name: string, file_name: string, is_sRGB: b
|
||||||
engine.renderer.enqueue proc()=
|
engine.renderer.enqueue proc()=
|
||||||
try:
|
try:
|
||||||
self.ensure_storage()
|
self.ensure_storage()
|
||||||
self.loadFileFromPointersLen(@[(p, len)], loadFromPixels)
|
self.loadFileFromPointersLen(@[(p, len)])
|
||||||
self.loaded = true
|
self.loaded = true
|
||||||
except:
|
except:
|
||||||
# TODO: use logging
|
# TODO: use logging
|
||||||
|
@ -518,6 +657,27 @@ proc setExtrapolation*(self: Texture, ext: TextureExtrapolation) =
|
||||||
glTexParameteri(self.storage.target, GL_TEXTURE_WRAP_T, e.GLint)
|
glTexParameteri(self.storage.target, GL_TEXTURE_WRAP_T, e.GLint)
|
||||||
glTexParameteri(self.storage.target, GL_TEXTURE_WRAP_R, e.GLint)
|
glTexParameteri(self.storage.target, GL_TEXTURE_WRAP_R, e.GLint)
|
||||||
|
|
||||||
|
proc swap_lines(p: pointer, line_stride, line_count: int) =
|
||||||
|
template `+`(p: pointer, i: Natural): pointer = cast[pointer](cast[int](p) +% cast[int](i))
|
||||||
|
template `-`(p: pointer, i: Natural): pointer = cast[pointer](cast[int](p) -% cast[int](i))
|
||||||
|
let int_stride = line_stride div sizeof(int)
|
||||||
|
let int_stride_bytes = int_stride * sizeof(int)
|
||||||
|
var p1 = p
|
||||||
|
var p2 = p + line_stride*(line_count-1)
|
||||||
|
var a1, a2: ptr UncheckedArray[int]
|
||||||
|
var b1, b2: ptr UncheckedArray[byte]
|
||||||
|
for i in 0 ..< line_count div 2:
|
||||||
|
a1 = cast[ptr UncheckedArray[int]](p1)
|
||||||
|
a2 = cast[ptr UncheckedArray[int]](p2)
|
||||||
|
b1 = cast[ptr UncheckedArray[byte]](p1)
|
||||||
|
b2 = cast[ptr UncheckedArray[byte]](p2)
|
||||||
|
for j in 0 ..< int_stride:
|
||||||
|
swap(a1[j], a2[j])
|
||||||
|
for j in int_stride_bytes ..< line_stride:
|
||||||
|
swap(b1[j], b2[j])
|
||||||
|
p1 = p1 + line_stride
|
||||||
|
p2 = p2 - line_stride
|
||||||
|
|
||||||
proc getTexturePixels*(self: Texture): TexturePixels =
|
proc getTexturePixels*(self: Texture): TexturePixels =
|
||||||
when false:
|
when false:
|
||||||
var len_bytes, cube_stride = self.width * self.height * self.format.stride
|
var len_bytes, cube_stride = self.width * self.height * self.format.stride
|
||||||
|
|
Loading…
Reference in a new issue