Refactor so process-tokens gets individual template lines

(defstruct param type name deref)
(defstruct funcdef funcname params returntype)

(setq begin-marker "^^^BEGINREPLACE^^^")
(setq end-marker "^^^ENDREPLACE^^^")
(setq auto-header-marker "^^^AUTOGENHEADER^^^")
(setq token-marker #\^)
(setq token-arg-marker #\:)
(setq token-arg-delimiter #\,)
(setq voidparam (make-param :type "void*" :name "service"))

(defstruct lppmacro macro filter)

(setq macro-hash (make-hash-table :test 'equal))
(defun add-macro (&rest args)
    (let ((m (apply #'make-lppmacro args)))
        (setf (gethash (lppmacro-macro m) macro-hash) m)
    )
)

(add-macro :macro "RETURN"	:filter (lambda (fd args) (funcdef-returntype fd)))
(add-macro :macro "FUNC"	:filter (lambda (fd args) (funcdef-funcname fd)))
(add-macro :macro "PARAMS"	:filter (lambda (fd args) (format-params (funcdef-params fd) args)))
; PARAMS arguments: types, names, deref, ref, void
;;;;;;;;;

(defun partial (func &rest args1)
   (lambda (&rest args2) (apply func (append args1 args2)))
)

(defun newline ()
	(write-string (format nil "~%"))
)

(defun concat-string-list (s)
	(format nil "~{~A~^~%~}" s)
)

(defun replace-all (string part replacement &key (test #'char=))
"Returns a new string in which all the occurences of the part
is replaced with replacement."
    (with-output-to-string (out)
      (loop with part-length = (length part)
            for old-pos = 0 then (+ pos part-length)
            for pos = (search part string
                              :start2 old-pos
                              :test test)
            do (write-string string out
                             :start old-pos
                             :end (or pos (length string)))
            when pos do (write-string replacement out)
            while pos))
)

(defun is-arg-set (target-arg args)
    (not (null (find target-arg args :test 'equal)))
)

(defun format-paramitem-type (args p)
	(format nil "~A" (param-type p))
)

(defun format-paramitem-name (args p)
    ;in name only format, we can dereference the argument (pointer)
    ;but only if the macro argument requested it
    ;and this parameter can be dereferenced
	(if (and (is-arg-set "deref" args) (param-deref p))
		;then
		(format nil "*~A" (param-name p))
		;else
		(format nil "~A" (param-name p))
	)
)

(defun format-paramitem-full (args p)
	(let* ((type-decl (param-type p))
           (last-index (1- (length type-decl)))
          )
        (format nil "~A ~A"
                ;if macro requested reference mode,
                ; this parameter is dereferencable,
                ; and the last char is a *
                (if (and (is-arg-set "ref" args) (param-deref p) (equal "*" (subseq type-decl last-index)))
                    ;then, replace the last * with a &
                    (concatenate 'string (subseq type-decl 0 last-index) "&")
                    ;else
                    type-decl
                )
                (param-name p))
	)
)

(defun format-params (params args)
    ;full, types, names, deref, ref, void
    (let*  ((arg-types (is-arg-set "types" args))
            (arg-names (is-arg-set "names" args))
            (arg-deref (is-arg-set "deref" args))
            (arg-ref   (is-arg-set "ref" args))
            (arg-void  (is-arg-set "void" args))
            (filtered-params (if arg-void
                                 (cons voidparam params) ;then, prepend "void* service"
                                 params                  ;else, don't
                              ))
            (types-only (and arg-types (not arg-names)))
            (names-only (and arg-names (not arg-types)))
            (full-decl  (not (xor arg-names arg-types)))
            (format-func (cond  (full-decl  #'format-paramitem-full)
                                (types-only #'format-paramitem-type)
                                (names-only #'format-paramitem-name)
                          ))
           )
        (format nil "~{~A~^, ~}" (mapcar (partial format-func args) filtered-params))
    )
)

(defun split-by-delimiter (string delimiter)
	(loop 	for start = 0 then (1+ finish)
			for finish = (position delimiter string :start start)
			if (and (not (eq (length string) start)) (not (eq start finish))) collect (subseq string start finish)
			until (null finish)
	)
)

(defun process-tokens (line funcdata)
    (let ((token-marker-left (position token-marker line))
         )
        (if (null token-marker-left)
            ;then, no left token marker. return line
            line
            ;else, process more
            (let* ((token-start (1+ token-marker-left))
                  (token-stop (position token-marker line :start token-start))
                 )
                (if (null token-stop)
                    ;then, no right token marker. return line
                    line
                    ;else, process more

        (let* ((token-full (subseq line token-start token-stop))
               (seperator-index (position token-arg-marker token-full))
               (token (subseq token-full 0 seperator-index))
               (token-args (if (not (null seperator-index))
                              (split-by-delimiter (subseq token-full (1+ seperator-index) nil) token-arg-delimiter)
                              '(nil)
                          ))
               (line-begin (subseq line 0 token-marker-left))
               (line-end (subseq line (1+ token-stop) nil))
               (m (gethash token macro-hash))
              )
              ;m (the macro struct) is nil if no macro found for the token
              (if (null m)
                    ;then
                    line
                    ;else
                    (concatenate 'string
                        line-begin
                        (funcall (lppmacro-filter m) funcdata token-args)
                        (process-tokens line-end funcdata)
                    )
               )
        )
    ))))
)

(defun expand-template (template-list func-data)
    (concat-string-list
        (mapcar
            (lambda (template-line) (process-tokens template-line func-data))
            template-list
        )
    )
)

(defun expand-methods-with-template (template-list func-data-list)
    (mapcar
		(lambda (func-data) (expand-template template-list func-data))
        func-data-list
	)
)

(defun clear-funcs ()
	(setq funcs nil)
	(setq funcs-tail funcs)
)
(clear-funcs)

(defun add-func (&rest args)
	(let ((func-data (apply #'make-funcdef args)))
		(if (endp funcs)
			;then
			(progn
				(setq funcs (cons func-data nil))
				(setq funcs-tail funcs)
			)
			;else
			(progn
				(setf (cdr funcs-tail) (cons func-data nil))
				(setq funcs-tail (cdr funcs-tail))
			)
		)
	)
)

(load "apidef.cl")

(defun outputfuncs (f)
	(write-line (concat-string-list f))
)

(defun filter-line (line filename)
	(replace-all line auto-header-marker (format nil "THIS FILE AUTO-GENERATED FROM ~A. DO NOT EDIT." filename))
)

(defmacro prepend-into (var value)
	`(setq ,var (cons ,value ,var))
)

(defun strip-end-characters (str numChars)
	(let ((index (- (length str) numChars)))
		(if (>= index 1)
			(subseq str 0 index)
			nil
		)
	)
)

(defun process-file (infilename)
	(let ((infile (open infilename :if-does-not-exist nil))
		  (outfile (open (strip-end-characters infilename 4) :direction :output :if-exists :supersede))
		  (filelines nil)
		  (templatelines nil)
		  (template-status 0)
		  (doneparts ())
		  )
	  (when infile
		(loop
			for line = (read-line infile nil)
			while line
			do
				(if (= template-status 0)
					(if (equal begin-marker line)
						;then
						(progn
							(setq template-status 1)
						)
						;else
						(prepend-into filelines (filter-line line infilename))
					)
					(if (equal end-marker line)
						;then
						(progn
							(setq template-status 0)
							;expand template
							(mapcar
								(lambda (v) (prepend-into filelines v))
								(expand-methods-with-template (reverse templatelines) funcs)
							)
						)
						;else
						(prepend-into templatelines line)
					)
				)
		)
		(loop for line in (reverse filelines)
			  do (write-line line outfile)
		)
		(close infile)
		(close outfile))
	)
)

(defun _ () (load "lpp.cl" :verbose nil))

(defun t2 ()
	(process-file "SenseKit.cpp.lpp")
	(process-file "StreamServiceProxyBase.h.lpp")
	"done"
)
(t2)

(defstruct param type name deref)
(defstruct funcdef funcname params returntype)

(setq begin-marker "^^^BEGINREPLACE^^^")
(setq end-marker "^^^ENDREPLACE^^^")
(setq auto-header-marker "^^^AUTOGENHEADER^^^")
(setq token-marker #\^)
(setq token-arg-marker #\:)
(setq token-arg-delimiter #\,)
(setq voidparam (make-param :type "void*" :name "service"))

(defstruct lppmacro macro filter)

(setq macro-hash (make-hash-table :test 'equal))
(defun add-macro (&rest args)
    (let ((m (apply #'make-lppmacro args)))
        (setf (gethash (lppmacro-macro m) macro-hash) m)
    )
)

(add-macro :macro "RETURN"	:filter (lambda (fd args) (funcdef-returntype fd)))
(add-macro :macro "FUNC"	:filter (lambda (fd args) (funcdef-funcname fd)))
(add-macro :macro "PARAMS"	:filter (lambda (fd args) (format-params (funcdef-params fd) args)))
; PARAMS arguments: types, names, deref, ref, void
;;;;;;;;;

(defun partial (func &rest args1)
   (lambda (&rest args2) (apply func (append args1 args2)))
)

(defun newline ()
	(write-string (format nil "~%"))
)

(defun concat-string-list (s)
	(format nil "~{~A~^~%~}" s)
)

(defun replace-all (string part replacement &key (test #'char=))
"Returns a new string in which all the occurences of the part
is replaced with replacement."
    (with-output-to-string (out)
      (loop with part-length = (length part)
            for old-pos = 0 then (+ pos part-length)
            for pos = (search part string
                              :start2 old-pos
                              :test test)
            do (write-string string out
                             :start old-pos
                             :end (or pos (length string)))
            when pos do (write-string replacement out)
            while pos))
)

(defun is-arg-set (target-arg args)
    (not (null (find target-arg args :test 'equal)))
)

(defun format-paramitem-type (args p)
	(format nil "~A" (param-type p))
)

(defun format-paramitem-name (args p)
    ;in name only format, we can dereference the argument (pointer)
    ;but only if the macro argument requested it
    ;and this parameter can be dereferenced
	(if (and (is-arg-set "deref" args) (param-deref p))
		;then
		(format nil "*~A" (param-name p))
		;else
		(format nil "~A" (param-name p))
	)
)

(defun format-paramitem-full (args p)
	(let* ((type-decl (param-type p))
           (last-index (1- (length type-decl)))
          )
        (format nil "~A ~A"
                ;if macro requested reference mode,
                ; this parameter is dereferencable,
                ; and the last char is a *
                (if (and (is-arg-set "ref" args) (param-deref p) (equal "*" (subseq type-decl last-index)))
                    ;then, replace the last * with a &
                    (concatenate 'string (subseq type-decl 0 last-index) "&")
                    ;else
                    type-decl
                )
                (param-name p))
	)
)

(defun format-params (params args)
    ;full, types, names, deref, ref, void
    (let*  ((arg-types (is-arg-set "types" args))
            (arg-names (is-arg-set "names" args))
            (arg-deref (is-arg-set "deref" args))
            (arg-ref   (is-arg-set "ref" args))
            (arg-void  (is-arg-set "void" args))
            (filtered-params (if arg-void
                                 (cons voidparam params) ;then, prepend "void* service"
                                 params                  ;else, don't
                              ))
            (types-only (and arg-types (not arg-names)))
            (names-only (and arg-names (not arg-types)))
            (full-decl  (not (xor arg-names arg-types)))
            (format-func (cond  (full-decl  #'format-paramitem-full)
                                (types-only #'format-paramitem-type)
                                (names-only #'format-paramitem-name)
                          ))
           )
        (format nil "~{~A~^, ~}" (mapcar (partial format-func args) filtered-params))
    )
)

(defun split-by-delimiter (string delimiter)
	(loop 	for start = 0 then (1+ finish)
			for finish = (position delimiter string :start start)
			if (and (not (eq (length string) start)) (not (eq start finish))) collect (subseq string start finish)
			until (null finish)
	)
)

(defun process-tokens (line funcdata)
    (let ((token-marker-left (position token-marker line))
         )
        (if (null token-marker-left)
            ;then, no left token marker. return line
            line
            ;else, process more
            (let* ((token-start (1+ token-marker-left))
                  (token-stop (position token-marker line :start token-start))
                 )
                (if (null token-stop)
                    ;then, no right token marker. return line
                    line
                    ;else, process more

        (let* ((token-full (subseq line token-start token-stop))
               (seperator-index (position token-arg-marker token-full))
               (token (subseq token-full 0 seperator-index))
               (token-args (if (not (null seperator-index))
                              (split-by-delimiter (subseq token-full (1+ seperator-index) nil) token-arg-delimiter)
                              '(nil)
                          ))
               (line-begin (subseq line 0 token-marker-left))
               (line-end (subseq line (1+ token-stop) nil))
               (m (gethash token macro-hash))
              )
              ;m (the macro struct) is nil if no macro found for the token
              (if (null m)
                    ;then
                    line
                    ;else
                    (concatenate 'string
                        line-begin
                        (funcall (lppmacro-filter m) funcdata token-args)
                        (process-tokens line-end funcdata)
                    )
               )
        )
    ))))
)

(defun format-methods (lineformat funcdatalist)
	(mapcar
		(partial #'process-tokens lineformat) funcdatalist
	)
)

(defun clear-funcs ()
	(setq funcs nil)
	(setq funcs-tail funcs)
)
(clear-funcs)

(defun add-func (&rest args)
	(let ((func-data (apply #'make-funcdef args)))
		(if (endp funcs)
			;then
			(progn
				(setq funcs (cons func-data nil))
				(setq funcs-tail funcs)
			)
			;else
			(progn
				(setf (cdr funcs-tail) (cons func-data nil))
				(setq funcs-tail (cdr funcs-tail))
			)
		)
	)
)

(load "apidef.cl")

(defun outputfuncs (f)
	(write-line (concat-string-list f))
)

(defun filter-line (line filename)
	(replace-all line auto-header-marker (format nil "THIS FILE AUTO-GENERATED FROM ~A. DO NOT EDIT." filename))
)

(defmacro prepend-into (var value)
	`(setq ,var (cons ,value ,var))
)

(defun strip-end-characters (str numChars)
	(let ((index (- (length str) numChars)))
		(if (>= index 1)
			(subseq str 0 index)
			nil
		)
	)
)

(defun process-file (infilename)
	(let ((infile (open infilename :if-does-not-exist nil))
		  (outfile (open (strip-end-characters infilename 4) :direction :output :if-exists :supersede))
		  (filelines nil)
		  (templatelines nil)
		  (template-status 0)
		  (doneparts ())
		  )
	  (when infile
		(loop
			for line = (read-line infile nil)
			while line
			do
				(if (= template-status 0)
					(if (equal begin-marker line)
						;then
						(progn
							(setq template-status 1)
						)
						;else
						(prepend-into filelines (filter-line line infilename))
					)
					(if (equal end-marker line)
						;then
						(progn
							(setq template-status 0)
							;expand template
							(mapcar
								(lambda (v) (prepend-into filelines v))
								(format-methods (concat-string-list (reverse templatelines)) funcs)
							)
						)
						;else
						(prepend-into templatelines line)
					)
				)
		)
		(loop for line in (reverse filelines)
			  do (write-line line outfile)
		)
		(close infile)
		(close outfile))
	)
)

(defun _ () (load "lpp.cl" :verbose nil))

(defun t2 ()
	(process-file "SenseKit.cpp.lpp")
	(process-file "StreamServiceProxyBase.h.lpp")
	"done"
)
(t2)


Fixed missing perenthesis.

Fixed missing perenthesis.

/*
 * Copyright (c) 2017 AMD Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and/or associated documentation files (the
 * "Materials"), to deal in the Materials without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Materials, and to
 * permit persons to whom the Materials are 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 Materials.
 *
 * THE MATERIALS ARE 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
 * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 */

#define _FLOAT					float
#define _FLOAT2					float2
#define _FLOAT4					float4
#define _FLOAT8					float8

#ifndef FLT_MAX
#define FLT_MAX         3.402823466e+38F        /* max value */
#endif

#define UNUSED __attribute__((__unused__))

#define DBG_OUT_OF_RNGE 0
// number of filter taps in the processing wk_item
//#define MLO_WEI_WKITEM 5

#define MLO_N_OUT_HORIZ_READS (MLO_ALIGNED_OUT_SCAN_LN)
#define MLO_OUT_HORIZ_PIX_SZ (MLO_N_OUT_HORIZ_READS * MLO_READ_UNIT)

// n of of filter blks
#define MLO_WEI_BLK_SZ0 ((MLO_FILTER_SIZE0 + MLO_WEI_WKITEM -1)/MLO_WEI_WKITEM)
#define MLO_WEI_BLK_SZ (MLO_FILTER_SIZE1*MLO_WEI_BLK_SZ0)
// n of filter tiles in the group grid
#define MLO_N_WEI_BLK (MLO_GRP_SZ/ MLO_WEI_BLK_SZ)
// n of steps per scan line to be made in the inner loop
// extended scan to deal with overshot in the inner loop
#define MLO_OUT_WEI_EXT_SCAN_BLK ((MLO_OUT_WIDTH + MLO_N_WEI_BLK - 1) / MLO_N_WEI_BLK)

#define MLO_OUT_WEI_SCAN_BLK (MLO_OUT_WEI_EXT_SCAN_BLK)

// max loop over virtual blocks for small images
#define MLO_MAX_WEI_BLK_LOOP_TMP ((MLO_OUT_WIDTH + MLO_OUT_WEI_SCAN_BLK - 1)/MLO_OUT_WEI_SCAN_BLK)
#if MLO_MAX_WEI_BLK_LOOP_TMP < MLO_N_WEI_BLK
#define MLO_MAX_WEI_BLK_LOOP MLO_MAX_WEI_BLK_LOOP_TMP
#else
#define MLO_MAX_WEI_BLK_LOOP MLO_N_WEI_BLK
#endif

#define MLO_WEI_BLKS_SZ (MLO_MAX_WEI_BLK_LOOP * MLO_WEI_BLK_SZ *MLO_WEI_WKITEM)
#define MLO_WEI_BLKS_LCL_SZ (MLO_WEI_BLKS_SZ * MLO_N_LCL_OUT_MAPS)

#define MLO_OUT_BLK_GRP_PIX_SZ (MLO_OUT_HORIZ_PIX_SZ * MLO_N_ALIGNED_OUT_SCAN_BLK)
#define MLO_OUT_BLK_GRP_WK_SZ (MLO_OUT_BLK_GRP_PIX_SZ / MLO_READ_UNIT)

#if MLO_OUT_HORIZ_PIX_SZ <  (MLO_OUT_WEI_EXT_SCAN_BLK * MLO_MAX_WEI_BLK_LOOP)
#define MLO_OUT_HORIZ_PIX_EXT_SZ (MLO_OUT_WEI_EXT_SCAN_BLK * MLO_MAX_WEI_BLK_LOOP)
#else
#define MLO_OUT_HORIZ_PIX_EXT_SZ MLO_OUT_HORIZ_PIX_SZ
#endif
#define MLO_OUT_BLK_GRP_EXT_PIX_SZ (MLO_OUT_HORIZ_PIX_EXT_SZ * MLO_N_ALIGNED_OUT_SCAN_BLK)
#define MLO_OUT_LCL_SZ (MLO_OUT_BLK_GRP_EXT_PIX_SZ)
// LDS OUT SIZE
#define MLO_TOTAL_OUT_LCL_SZ (MLO_N_LCL_BATCHS*MLO_N_LCL_OUT_MAPS*MLO_OUT_LCL_SZ)
#if ((MLO_OUT_HEIGHT/MLO_N_ALIGNED_OUT_SCAN_BLK)*MLO_N_ALIGNED_OUT_SCAN_BLK == MLO_OUT_HEIGHT)
#define MLO_BLK_ALIGNED 1
#else
#define MLO_BLK_ALIGNED 0
#endif

// input size depends on output scan length and
// number of output scans
// this number is constrained by amount or LDS and size of register file.
// TO DO:: CHECK PADDING!!!
#define MLO_IN_LCL_HEIGHT ((MLO_N_ALIGNED_OUT_SCAN_BLK-1)*MLO_FILTER_STRIDE1 + MLO_FILTER_SIZE1)
// there is an assumption that the scanline fits into LDS
#define MLO_N_IN_HORIZ_PIX_READS (MLO_IN_WIDTH) //((MLO_OUT_WIDTH-1)*MLO_FILTER_STRIDE0 + MLO_FILTER_SIZE0 - 2 * MLO_FILTER_PAD0)
#define MLO_N_IN_HORIZ_READS ((MLO_N_IN_HORIZ_PIX_READS + MLO_READ_UNIT - 1) / MLO_READ_UNIT)
#define MLO_IN_N_PIXS_OFF  (MLO_N_IN_HORIZ_PIX_READS - (MLO_N_IN_HORIZ_PIX_READS  / MLO_READ_UNIT)*MLO_READ_UNIT)

#define MLO_IN_LCL_WIDTH (MLO_N_IN_HORIZ_READS * MLO_READ_UNIT + 2 * MLO_FILTER_PAD0)
#define MLO_IN_BLK_GRP_PIX_SZ (MLO_IN_LCL_WIDTH * MLO_IN_LCL_HEIGHT)
#define MLO_IN_BLK_GRP_WK_SZ (MLO_IN_BLK_GRP_PIX_SZ/MLO_READ_UNIT)
#define MLO_IN_LCL_SZ (MLO_IN_BLK_GRP_PIX_SZ)
// LDS IN SIZE
#define MLO_TOTAL_IN_LCL_SZ (MLO_N_LCL_BATCHS*MLO_N_LCL_IN_MAPS*MLO_IN_LCL_SZ)
#define MLO_IN_VERT_READS (MLO_GRP_SZ/MLO_N_IN_HORIZ_READS)

#if (MLO_TOTAL_OUT_LCL_SZ + MLO_TOTAL_IN_LCL_SZ) > (MLO_WEI_BLKS_LCL_SZ)
#define MLO_LCL_SZ (MLO_TOTAL_OUT_LCL_SZ + MLO_TOTAL_IN_LCL_SZ)
#else
#define MLO_LCL_SZ (MLO_WEI_BLKS_LCL_SZ)
#endif

__attribute__((always_inline))
uint iDiv(uint v, uint d)
{
	uint r = (uint)((float)v * (1.0f/ (float)d) + 0.00001f);
	return(r);
}

__attribute__((always_inline))
uint iMod(uint v, uint u, uint d)
{
	uint r = v - mul24((uint)u, (uint)d);
	return(r);
}

/*********************************************************************************************************
// wrw algorithm for large filters
// idea:
// split filter taps into sub-tiles along x and y axis with number of tap groups muliples of stride or 1
// for example
// the 5x10 filter has been split into 10 sub-tiles 1x5 each, 1 tap in y direction and 5 taps in x direction.
// those horizontal taps are 0, 2, 4, 6, 8 and 1, 3, 5, 7, 9
// a single vertical tap is 0 or 1 or 2 or 3 or 4.
// one may say sub-tiles are indexed by a vertical tap.
// the partial sum has been calculated into those 10 sub-tiles in parallel.
// the full filter has been calulated by reducing all sub-tiles into a single filter per group.
// teh accumulation has been done over all pixels of several outputs being shared with a single input.
// the accuulation has been done per batch.
//
// the total reduction over all batches has been doesn a separete kerenel.
//
// alg
//
//		until end of output map (MLO_N_OUT_BLK)
//			load input map block in LDS
//			load output maps in LDS
//          for j in output scans
//				for i in output scan interval
//                  accumulate the weights into sub-tiles
//
//		reduce sub-tiles into a single filter for each output
//		write accululated weights
//
// group layout
// 0 - n waves * wave size (n_waves has been defined by host)
// 1 - input channel index
// 2 - output channel/batch index
//
//
// for each batch
//	 accumulate all weights per input/output pair

**********************************************************************************************************/

__attribute__((reqd_work_group_size(MLO_GRP_SZ0, MLO_GRP_SZ1, MLO_GRP_SZ2)))
__kernel void MIOpenCvBwdWrW(
	const __global _FLOAT * __restrict top_df,
	const __global _FLOAT * __restrict bot,
	__global _FLOAT * __restrict weights_df,
#if MLO_CONV_BIAS
	__global _FLOAT * __restrict bias_df,
#endif
	UNUSED _FLOAT padding_val
)
{

	// input/output tiles + reduce buffer

	__local _FLOAT lcl[(MLO_LCL_SZ)];
	__local _FLOAT * lcl_bot = lcl;
	__local _FLOAT * lcl_top = lcl + MLO_TOTAL_IN_LCL_SZ;

	uint c_idx_base = get_group_id(0); // input map index base

	uint o_idx_base = get_group_id(1); // output map index base
	uint ib_base = get_group_id(2); // batch index base

	uint ib = ib_base*MLO_N_LCL_BATCHS;

	uint c_idx = c_idx_base * MLO_N_LCL_IN_MAPS; // input map index

	uint o_idx = o_idx_base * (MLO_N_OUT_BLK_GRP * MLO_N_LCL_OUT_MAPS); // output map index

	uint gbl_in_off = c_idx * MLO_IN_CHANNEL_STRIDE + ib * MLO_IN_BATCH_STRIDE;
	uint gbl_out_off = o_idx * MLO_OUT_CHANNEL_STRIDE + ib * MLO_OUT_BATCH_STRIDE;

	uint lcl_id = get_local_id(0);

// weight tile id
#if MLO_WEI_BLK_SZ & (MLO_WEI_BLK_SZ - 1)
	uint w_blk_idx = iDiv(lcl_id, MLO_WEI_BLK_SZ);
	uint w_idx = iMod(lcl_id, w_blk_idx, MLO_WEI_BLK_SZ);
#else
	uint w_blk_idx = lcl_id / MLO_WEI_BLK_SZ;
	uint w_idx = lcl_id & (MLO_WEI_BLK_SZ - 1);
#endif
#if MLO_WEI_BLK_SZ0 & (MLO_WEI_BLK_SZ0 - 1)
// weight y
	uint w_y = iDiv(w_idx, MLO_WEI_BLK_SZ0);
// weight starting x
	uint w_x0 = iMod(w_idx, w_y, MLO_WEI_BLK_SZ0);
#else
	uint w_y = w_idx / MLO_WEI_BLK_SZ0;
	uint w_x0 = w_idx & (MLO_WEI_BLK_SZ0 - 1);
#endif

	__private _FLOAT pvt_accum[(MLO_N_OUT_BLK_GRP * MLO_N_LCL_OUT_MAPS * MLO_WEI_WKITEM)];

	for (uint i = 0; i < (MLO_N_OUT_BLK_GRP * MLO_N_LCL_OUT_MAPS * MLO_WEI_WKITEM); ++i)
	{
		pvt_accum[i] = 0;
	}

	// zero out LDS
	for (uint i = lcl_id; i < (MLO_LCL_SZ); i += MLO_GRP_SZ)
	{
		lcl[i] = 0;
	}

	// over all batches

	for (uint b = 0;
		b < MLO_N_BATCH_LOOPS;
		++b,
		gbl_in_off += MLO_N_LCL_BATCHS*MLO_IN_BATCH_STRIDE,
		gbl_out_off += MLO_N_LCL_BATCHS*MLO_OUT_BATCH_STRIDE
		)
	{

		barrier(CLK_LOCAL_MEM_FENCE);

		uint in_y = 0;
		uint out_y = 0;

// prefetch MLO_FILTER_STRIDE1 - MLO_FILTER_PAD1 input scans
		__private _FLOAT in_rd_data[MLO_READ_UNIT];

		uint gbl_in_scan_off = gbl_in_off;
		uint gbl_out_scan_off = gbl_out_off;
		uint c_scan = 0;

		for (uint p4 = lcl_id;  p4 < MLO_N_IN_HORIZ_READS * (MLO_FILTER_SIZE1 - MLO_FILTER_STRIDE1 - MLO_FILTER_PAD1);
			p4 += MLO_GRP_SZ)
		{
#if MLO_N_IN_HORIZ_READS & (MLO_N_IN_HORIZ_READS - 1)
			c_scan = iDiv(p4, MLO_N_IN_HORIZ_READS);
			uint c_pix4 = iMod(p4, c_scan, MLO_N_IN_HORIZ_READS);
#else
			c_scan = p4 / MLO_N_IN_HORIZ_READS;
			uint c_pix4 = p4 & (MLO_N_IN_HORIZ_READS - 1);
#endif
			for (uint i = 0; i < MLO_READ_UNIT; ++i)
			{
				in_rd_data[i] = 0;
			}
			if (in_y + c_scan < MLO_IN_HEIGHT)
			{
				uint bot_off = gbl_in_scan_off + c_scan * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT;
				const __global _FLOAT * bot_p = &bot[bot_off];
				// still problems with unaligned LDS access
#if MLO_IN_N_PIXS_OFF > 0
				if (c_pix4 == MLO_N_IN_HORIZ_READS - 1)
				{
					uint i = 0;
					for (; i < MLO_IN_N_PIXS_OFF; ++i)
					{
						in_rd_data[i] = bot_p[i];
#if DBG_OUT_OF_RNGE
						if (bot_off + i >= MLO_IN_BATCH_STRIDE * MLO_BATCH_SZ)
						{
							printf("k:err:in-of-range\n");
						}
#endif
					}

				}
				else
#endif
				{
					for (uint i = 0; i < MLO_READ_UNIT; ++i)
					{
						in_rd_data[i] = bot_p[i];
#if DBG_OUT_OF_RNGE
						if (bot_off + i >= MLO_IN_BATCH_STRIDE * MLO_BATCH_SZ)
						{
							printf("k:err:in-of-range\n");
						}
#endif
					}

				}
			}
			for (uint i = 0; i < MLO_READ_UNIT; ++i)
			{
				lcl_bot[(c_scan + MLO_FILTER_PAD1)*MLO_IN_LCL_WIDTH + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT + i] = in_rd_data[i];
			}

		}

		in_y += MLO_FILTER_SIZE1 - MLO_FILTER_STRIDE1 - MLO_FILTER_PAD1;

// TO DO: HANDLE PADDING
// over all out blocks
// processing per MLO_N_ALIGNED_OUT_SCAN_BLK output scans

		for (uint ob = 0; ob < MLO_N_OUT_BLK; ++ob, in_y += (MLO_IN_LCL_HEIGHT - MLO_FILTER_SIZE1 + MLO_FILTER_STRIDE1), out_y += MLO_N_ALIGNED_OUT_SCAN_BLK)
		{

			barrier(CLK_LOCAL_MEM_FENCE);

// fetch input: (MLO_IN_LCL_HEIGHT - MLO_FILTER_SIZE1 + 1)
// TODO:: HANDLE multiple INPUTS
// an overshoot has to be handled by zero outing output
			gbl_in_scan_off = gbl_in_off + mul24(in_y, ( uint )MLO_IN_STRIDE);
			uint c_scan2 = 0;
			for (uint p4 = lcl_id; p4 < MLO_N_IN_HORIZ_READS * (MLO_IN_LCL_HEIGHT - MLO_FILTER_SIZE1 + MLO_FILTER_STRIDE1);
				p4 += MLO_GRP_SZ)
			{
#if MLO_N_IN_HORIZ_READS & (MLO_N_IN_HORIZ_READS - 1)
				c_scan2 = iDiv(p4, MLO_N_IN_HORIZ_READS);
				uint c_pix4 = iMod(p4, c_scan2, MLO_N_IN_HORIZ_READS);
#else
				c_scan2 = p4 / MLO_N_IN_HORIZ_READS;
				uint c_pix4 = p4 & (MLO_N_IN_HORIZ_READS - 1);
#endif

				for (uint i = 0; i < MLO_READ_UNIT; ++i)
				{
					in_rd_data[i] = 0;
				}

				if (in_y + c_scan2 < MLO_IN_HEIGHT)
				{
					uint bot_off = gbl_in_scan_off + c_scan2 * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT;
					const __global _FLOAT * bot_p = &bot[bot_off];

#if MLO_IN_N_PIXS_OFF > 0
					if (c_pix4 == MLO_N_IN_HORIZ_READS - 1)
					{

						uint i = 0;
						for (; i < MLO_IN_N_PIXS_OFF; ++i)
						{
							in_rd_data[i] = bot_p[i];
#if DBG_OUT_OF_RNGE
							if (bot_off + i >= MLO_IN_BATCH_STRIDE * MLO_BATCH_SZ)
							{
								printf("k:err:in-of-range\n");
							}
#endif
						}

					}
					else
#endif
					{

						for (uint i = 0; i < MLO_READ_UNIT; ++i)
						{
							in_rd_data[i] = bot_p[i];
#if DBG_OUT_OF_RNGE
							if (bot_off + i >= MLO_IN_BATCH_STRIDE * MLO_BATCH_SZ)
							{
								printf("k:err:in-of-range\n");
							}
#endif
						}
					}

				} // if (in_y + c_scan2 < MLO_IN_HEIGHT)

				for (uint i = 0; i < MLO_READ_UNIT; ++i)
				{
					uint lcl_off = (c_scan2 + MLO_FILTER_SIZE1 - MLO_FILTER_STRIDE1)*MLO_IN_LCL_WIDTH + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT;
					lcl_bot[lcl_off + i] = in_rd_data[i];
				}

			}

			gbl_out_scan_off = gbl_out_off + mul24(out_y, ( uint )MLO_OUT_STRIDE);

// over all outputs groups
// MLO_N_OUT_BLK_GRP outputs reuse the same input
// each output blk is MLO_N_LCL_OUT_MAPS outputs
// MLO_N_LCL_OUT_MAPS nuber is restricted by LDS size

			uint gbl_out_scan_off1 = gbl_out_scan_off;
			for (uint og = 0; og < MLO_N_OUT_BLK_GRP
				; ++og, gbl_out_scan_off1 += MLO_N_LCL_OUT_MAPS*MLO_OUT_CHANNEL_STRIDE)
			{
				barrier(CLK_LOCAL_MEM_FENCE);

// fetch output. MLO_N_ALIGNED_OUT_SCAN_BLK output scans, each of size MLO_N_OUT_HORIZ_READS

				__private _FLOAT out_rd_data[MLO_READ_UNIT];

				for (uint oo_p4 = lcl_id; oo_p4 < (MLO_N_LCL_OUT_MAPS*MLO_N_ALIGNED_OUT_SCAN_BLK*MLO_N_OUT_HORIZ_READS);
																											oo_p4 += MLO_GRP_SZ)
				{
#if (MLO_N_ALIGNED_OUT_SCAN_BLK*MLO_N_OUT_HORIZ_READS) & ((MLO_N_ALIGNED_OUT_SCAN_BLK*MLO_N_OUT_HORIZ_READS) - 1)
					uint o = iDiv(oo_p4, (MLO_N_ALIGNED_OUT_SCAN_BLK*MLO_N_OUT_HORIZ_READS));
					uint o_pX4 = iMod(oo_p4, o, (MLO_N_ALIGNED_OUT_SCAN_BLK*MLO_N_OUT_HORIZ_READS));
#else
					uint o = oo_p4 / (MLO_N_ALIGNED_OUT_SCAN_BLK*MLO_N_OUT_HORIZ_READS);
					uint o_pX4 = oo_p4 & ((MLO_N_ALIGNED_OUT_SCAN_BLK*MLO_N_OUT_HORIZ_READS) - 1);
#endif
#if MLO_N_OUT_HORIZ_READS & (MLO_N_OUT_HORIZ_READS - 1)
					uint o_scan = iDiv(o_pX4, MLO_N_OUT_HORIZ_READS);
					uint o_pix4 = iMod(o_pX4, o_scan, MLO_N_OUT_HORIZ_READS);
#else
					uint o_scan = o_pX4 / MLO_N_OUT_HORIZ_READS;
					uint o_pix4 = o_pX4 & (MLO_N_OUT_HORIZ_READS - 1);
#endif
					for (uint i = 0; i < MLO_READ_UNIT; ++i)
					{
						out_rd_data[i] = 0;
					}
					uint top_df_off = gbl_out_scan_off1 + o*MLO_OUT_CHANNEL_STRIDE + o_scan * MLO_OUT_STRIDE + o_pix4*MLO_READ_UNIT;
					const __global _FLOAT * top_df_p = &top_df[top_df_off];

					if (out_y + o_scan < MLO_OUT_HEIGHT
						&& o_idx + og *MLO_N_LCL_OUT_MAPS + o < MLO_N_OUTPUTS
						)
					{
// scan has been fetch by 4
// here the non-multiple of 4 scan has been handled
// also makes sure the input garbage hs been multipled by 0
#if MLO_OUT_N_PIXS_OFF > 0
						if (o_pix4 == (MLO_N_OUT_HORIZ_READS - 1))
						{
							uint i = 0;
							for (; i < MLO_OUT_N_PIXS_OFF; ++i)
							{
								out_rd_data[i] = top_df_p[i];
#if DBG_OUT_OF_RNGE
								if (top_df_off + i >= MLO_OUT_BATCH_STRIDE * MLO_BATCH_SZ)
								{
									printf("k:err:out-of-range\n");
								}
#endif
							}
						}

						else
#endif

						{
							for (uint i = 0; i < MLO_READ_UNIT; ++i)
							{
								out_rd_data[i] = top_df_p[i];
#if DBG_OUT_OF_RNGE
								if (top_df_off + i >= MLO_OUT_BATCH_STRIDE * MLO_BATCH_SZ)
								{
									printf("k:err:out-of-range\n");
								}
#endif
							}
						}

					} // if (out_y + o_scan < MLO_OUT_HEIGHT && o_idx + og *MLO_N_LCL_OUT_MAPS + o < MLO_N_OUTPUTS)

// write into LDS with MLO_OUT_HORIZ_PIX_EXT_SZ stride to zero out weights block overshoot

					for (uint i = 0; i < MLO_READ_UNIT; ++i)
					{
						uint lcl_off = o * MLO_OUT_LCL_SZ + o_scan * MLO_OUT_HORIZ_PIX_EXT_SZ + o_pix4*MLO_READ_UNIT;
						lcl_top[lcl_off + i] = out_rd_data[i];
					}

				} //	for (uint oo_p4 = lcl_id; oo_p4 < (MLO_N_LCL_OUT_MAPS*MLO_N_ALIGNED_OUT_SCAN_BLK*MLO_N_OUT_HORIZ_READS); oo_p4 += MLO_GRP_SZ)

				barrier(CLK_LOCAL_MEM_FENCE);

// process
// algorithm

					// over all input scans in LDS
					for (uint j = 0; j < MLO_N_ALIGNED_OUT_SCAN_BLK; ++j)
					{

						// prefetch proper inputs pixels.
						// they are MLO_WEI_BLK_SZ0 apart taps of the filter

						_FLOAT i_vals[MLO_WEI_WKITEM];

						for (uint w = 0; w < (MLO_WEI_WKITEM - (MLO_FILTER_STRIDE0 / MLO_WEI_BLK_SZ0)); ++w)
						{
							uint w_x = w_x0 + w*MLO_WEI_BLK_SZ0;
							uint i_off = (j*MLO_FILTER_STRIDE1 + w_y) * MLO_IN_LCL_WIDTH + (w_blk_idx*MLO_OUT_WEI_SCAN_BLK + 0) * MLO_FILTER_STRIDE0 + w_x;
							_FLOAT i_val = lcl_bot[i_off];

							i_vals[w] = i_val;

						}

						// if we overshoot the scanline
						// out data will be 0 by initial setting
						for (uint i = 0; i < MLO_OUT_WEI_SCAN_BLK; ++i)
						{

							// read the current input pixel
							for (uint w = (MLO_WEI_WKITEM - (MLO_FILTER_STRIDE0 / MLO_WEI_BLK_SZ0)); w < MLO_WEI_WKITEM; ++w)
							{
								uint w_x = w_x0 + w* MLO_WEI_BLK_SZ0;
								uint i_off = (j*MLO_FILTER_STRIDE1 + w_y) * MLO_IN_LCL_WIDTH + (w_blk_idx*MLO_OUT_WEI_SCAN_BLK + i) * MLO_FILTER_STRIDE0 + w_x;
								_FLOAT i_val = lcl_bot[i_off];

								i_vals[w] = i_val;
							}
							// for each output accumulate a proper filter tap
							for (uint o = 0; o < MLO_N_LCL_OUT_MAPS; ++o)
							{

								// read with MLO_OUT_HORIX_PIX_EXT_SZ stride
								_FLOAT o_val
									= lcl_top[(o)*MLO_OUT_LCL_SZ + j * MLO_OUT_HORIZ_PIX_EXT_SZ + (w_blk_idx*MLO_OUT_WEI_SCAN_BLK + i)];

								uint w = 0;
								_FLOAT i_val;
								for (/*uint w = 0*/; w < MLO_WEI_WKITEM; ++w)
								{

									i_val = i_vals[w];

									pvt_accum[(og * MLO_N_LCL_OUT_MAPS + o) * MLO_WEI_WKITEM + w] += i_val * o_val;

								} // for (/*uint w = 0*/; w < MLO_WEI_WKITEM; ++w)

							} // for (uint o = 0; o < MLO_N_LCL_OUT_MAPS; ++o)

							for (uint w = 0; w < (MLO_WEI_WKITEM - (MLO_FILTER_STRIDE0 / MLO_WEI_BLK_SZ0)); ++w)
							{
								i_vals[w] = i_vals[w + (MLO_FILTER_STRIDE0 / MLO_WEI_BLK_SZ0)];

							}
						} // for (uint i = 0; i < MLO_OUT_WEI_SCAN_BLK; ++i)
					} // for (uint j = 0; j < MLO_N_ALIGNED_OUT_SCAN_BLK; ++j)

			} // for(; og < (MLO_N_OUT_BLK_GRP; ++og )

// move the input data tail inside LDS to reduce mem bandwidth
			for (uint c_scan3 = 0; c_scan3 < (MLO_FILTER_SIZE1 - MLO_FILTER_STRIDE1); ++c_scan3)
			{
				barrier(CLK_LOCAL_MEM_FENCE);

				for (uint p4 = lcl_id; p4 < MLO_N_IN_HORIZ_READS;
												p4 += MLO_GRP_SZ)
				{

					uint c_pix4 = p4;
					for (uint i = 0; i < MLO_READ_UNIT; ++i)
					{
						lcl_bot[c_scan3*(MLO_IN_LCL_WIDTH)+MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT + i]
							= lcl_bot[(c_scan3 + (MLO_IN_LCL_HEIGHT - MLO_FILTER_SIZE1 + MLO_FILTER_STRIDE1))*(MLO_IN_LCL_WIDTH)+MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT + i];
					}

				}
			}

		} // for (uint ob = 0; ob < MLO_N_OUT_BLK; ++ob, in_y += (MLO_IN_LCL_HEIGHT - MLO_FILTER_SIZE1 + 1), out_y += MLO_N_ALIGNED_OUT_SCAN_BLK)
	} // for (uint b = 0;

// send it out

	uint wei_lcl_off = 0;
	_FLOAT final_sum = 0;

// save in lcl and orgnize in a proper order
	// outputs
	//	filter size1
	//	  filter size0

// TO DO:: DEPENDING ON THE GROUP SIZE
	for (uint og = 0; og < MLO_N_OUT_BLK_GRP; ++og)
	{
		barrier(CLK_LOCAL_MEM_FENCE);
		if ( w_blk_idx < MLO_MAX_WEI_BLK_LOOP)
		{
			for (uint o = 0; o < MLO_N_LCL_OUT_MAPS; ++o)
			{
				uint w = 0;
				for (; w < MLO_WEI_WKITEM; ++w)
				{
					// save "virtual" filter table
					uint w_x = w_x0 + w*MLO_WEI_BLK_SZ0;
					wei_lcl_off = ((o * MLO_MAX_WEI_BLK_LOOP + w_blk_idx) * MLO_FILTER_SIZE1 + w_y) * (MLO_WEI_BLK_SZ0 *MLO_WEI_WKITEM) + w_x;
					lcl[wei_lcl_off] = pvt_accum[(og * MLO_N_LCL_OUT_MAPS + o) * MLO_WEI_WKITEM + w];

				}

			}
		}

		barrier(CLK_LOCAL_MEM_FENCE);

		// read into real filter table

		for(uint l = lcl_id; l < (MLO_N_LCL_OUT_MAPS *MLO_WEI_CHANNEL_STRIDE); l+=MLO_GRP_SZ)
		{
#if MLO_WEI_CHANNEL_STRIDE & (MLO_WEI_CHANNEL_STRIDE - 1)
			uint oo = iDiv(l, MLO_WEI_CHANNEL_STRIDE);
			uint wei_i = iMod(l, oo, MLO_WEI_CHANNEL_STRIDE);
#else
			uint oo = l / MLO_WEI_CHANNEL_STRIDE;
			uint wei_i = l & MLO_WEI_CHANNEL_STRIDE - 1);
#endif
#if (MLO_FILTER_SIZE0) & ((MLO_FILTER_SIZE0) - 1)
			uint wei_i_y = iDiv(wei_i, (MLO_FILTER_SIZE0));
			uint wei_i_x = iMod(wei_i, wei_i_y, (MLO_FILTER_SIZE0));
#else
			uint wei_i_y = wei_i / (MLO_FILTER_SIZE0);
			uint wei_i_x = wei_i & ((MLO_FILTER_SIZE0) - 1);
#endif
			// send it out
			// inputs are outputs
			uint wei_df_off = ((ib * MLO_N_OUTPUTS + o_idx) * (uint)MLO_WEI_BATCH_STRIDE)
				// this input channel
				+ mul24(c_idx, (uint)MLO_WEI_CHANNEL_STRIDE);

			final_sum = 0;
			for (uint i = 0; i < MLO_MAX_WEI_BLK_LOOP; ++i)
			{
				final_sum += lcl_bot[((oo * MLO_MAX_WEI_BLK_LOOP + i) * MLO_FILTER_SIZE1 + wei_i_y) * (MLO_WEI_BLK_SZ0 *MLO_WEI_WKITEM) + wei_i_x];
			}

			weights_df[wei_df_off + (og *  MLO_N_LCL_OUT_MAPS + oo) * MLO_WEI_BATCH_STRIDE + wei_i] = final_sum; //lcl_bot[lcl_id]; //
		}

	} // for(uint og = 0; og < MLO_N_OUT_BLK_GRP; ++og)

}

// final reduction kernel
// add filters over batches
__attribute__((reqd_work_group_size(MLO_UT_GRP_SZ0, 1, 1)))
__kernel void MIOpenCvBwdWrW_rdc(
	const __global _FLOAT * __restrict weight_df_tmp,
	__global _FLOAT * __restrict weights_df
)
{
	uint gbl_id = get_global_id(0);
	uint wei_idx0 = gbl_id * MLO_UT_READ_UNIT;

#if MLO_WEI_CHANNEL_STRIDE & (MLO_WEI_CHANNEL_STRIDE - 1)
	uint wei_blk_idx = iDiv(wei_idx0, MLO_WEI_CHANNEL_STRIDE);
	uint wei_idx = iMod(wei_idx0, wei_blk_idx, MLO_WEI_CHANNEL_STRIDE);
#else
	uint wei_blk_idx = wei_idx0 / MLO_WEI_CHANNEL_STRIDE;
	uint wei_idx = wei_idx0 & (MLO_WEI_CHANNEL_STRIDE - 1);
#endif

	_FLOAT pvt_accum_wei[MLO_UT_READ_UNIT];
	for (uint i = 0; i < MLO_UT_READ_UNIT; ++i)
	{
		pvt_accum_wei[i] = 0;
	}

	uint batch_loop = (MLO_BATCH_SZ + (MLO_N_BATCH_LOOPS*MLO_N_LCL_BATCHS) - 1) / (MLO_N_BATCH_LOOPS*MLO_N_LCL_BATCHS);
	for (uint i = 0; i < batch_loop; ++i)
	{
		for (uint j = 0; j < MLO_UT_READ_UNIT; ++j)
		{
			pvt_accum_wei[j]
				+= weight_df_tmp[(wei_blk_idx * MLO_WEI_CHANNEL_STRIDE + i* MLO_N_OUTPUTS*MLO_WEI_BATCH_STRIDE) + wei_idx + j];
		}
	}

	for (uint j = 0; j < MLO_UT_READ_UNIT; ++j)
	{
		weights_df[wei_idx0 + j] = pvt_accum_wei[j];
	}

}

/*
 * Copyright (c) 2017 AMD Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and/or associated documentation files (the
 * "Materials"), to deal in the Materials without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Materials, and to
 * permit persons to whom the Materials are 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 Materials.
 *
 * THE MATERIALS ARE 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
 * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 */

#define _FLOAT					float
#define _FLOAT2					float2
#define _FLOAT4					float4
#define _FLOAT8					float8

#ifndef FLT_MAX
#define FLT_MAX         3.402823466e+38F        /* max value */
#endif

#define UNUSED __attribute__((__unused__))

#define DBG_OUT_OF_RNGE 0
// number of filter taps in the processing wk_item
//#define MLO_WEI_WKITEM 5

#define MLO_N_OUT_HORIZ_READS (MLO_ALIGNED_OUT_SCAN_LN)
#define MLO_OUT_HORIZ_PIX_SZ (MLO_N_OUT_HORIZ_READS * MLO_READ_UNIT)

// n of of filter blks
#define MLO_WEI_BLK_SZ0 ((MLO_FILTER_SIZE0 + MLO_WEI_WKITEM -1)/MLO_WEI_WKITEM)
#define MLO_WEI_BLK_SZ (MLO_FILTER_SIZE1*MLO_WEI_BLK_SZ0)
// n of filter tiles in the group grid
#define MLO_N_WEI_BLK (MLO_GRP_SZ/ MLO_WEI_BLK_SZ)
// n of steps per scan line to be made in the inner loop
// extended scan to deal with overshot in the inner loop
#define MLO_OUT_WEI_EXT_SCAN_BLK ((MLO_OUT_WIDTH + MLO_N_WEI_BLK - 1) / MLO_N_WEI_BLK)

#define MLO_OUT_WEI_SCAN_BLK (MLO_OUT_WEI_EXT_SCAN_BLK)

// max loop over virtual blocks for small images
#define MLO_MAX_WEI_BLK_LOOP_TMP ((MLO_OUT_WIDTH + MLO_OUT_WEI_SCAN_BLK - 1)/MLO_OUT_WEI_SCAN_BLK)
#if MLO_MAX_WEI_BLK_LOOP_TMP < MLO_N_WEI_BLK
#define MLO_MAX_WEI_BLK_LOOP MLO_MAX_WEI_BLK_LOOP_TMP
#else
#define MLO_MAX_WEI_BLK_LOOP MLO_N_WEI_BLK
#endif

#define MLO_WEI_BLKS_SZ (MLO_MAX_WEI_BLK_LOOP * MLO_WEI_BLK_SZ *MLO_WEI_WKITEM)
#define MLO_WEI_BLKS_LCL_SZ (MLO_WEI_BLKS_SZ * MLO_N_LCL_OUT_MAPS)

#define MLO_OUT_BLK_GRP_PIX_SZ (MLO_OUT_HORIZ_PIX_SZ * MLO_N_ALIGNED_OUT_SCAN_BLK)
#define MLO_OUT_BLK_GRP_WK_SZ (MLO_OUT_BLK_GRP_PIX_SZ / MLO_READ_UNIT)

#if MLO_OUT_HORIZ_PIX_SZ <  (MLO_OUT_WEI_EXT_SCAN_BLK * MLO_MAX_WEI_BLK_LOOP)
#define MLO_OUT_HORIZ_PIX_EXT_SZ (MLO_OUT_WEI_EXT_SCAN_BLK * MLO_MAX_WEI_BLK_LOOP)
#else
#define MLO_OUT_HORIZ_PIX_EXT_SZ MLO_OUT_HORIZ_PIX_SZ
#endif
#define MLO_OUT_BLK_GRP_EXT_PIX_SZ (MLO_OUT_HORIZ_PIX_EXT_SZ * MLO_N_ALIGNED_OUT_SCAN_BLK)
#define MLO_OUT_LCL_SZ (MLO_OUT_BLK_GRP_EXT_PIX_SZ)
// LDS OUT SIZE
#define MLO_TOTAL_OUT_LCL_SZ (MLO_N_LCL_BATCHS*MLO_N_LCL_OUT_MAPS*MLO_OUT_LCL_SZ)
#if ((MLO_OUT_HEIGHT/MLO_N_ALIGNED_OUT_SCAN_BLK)*MLO_N_ALIGNED_OUT_SCAN_BLK == MLO_OUT_HEIGHT)
#define MLO_BLK_ALIGNED 1
#else
#define MLO_BLK_ALIGNED 0
#endif

// input size depends on output scan length and
// number of output scans
// this number is constrained by amount or LDS and size of register file.
// TO DO:: CHECK PADDING!!!
#define MLO_IN_LCL_HEIGHT ((MLO_N_ALIGNED_OUT_SCAN_BLK-1)*MLO_FILTER_STRIDE1 + MLO_FILTER_SIZE1)
// there is an assumption that the scanline fits into LDS
#define MLO_N_IN_HORIZ_PIX_READS (MLO_IN_WIDTH) //((MLO_OUT_WIDTH-1)*MLO_FILTER_STRIDE0 + MLO_FILTER_SIZE0 - 2 * MLO_FILTER_PAD0)
#define MLO_N_IN_HORIZ_READS ((MLO_N_IN_HORIZ_PIX_READS + MLO_READ_UNIT - 1) / MLO_READ_UNIT)
#define MLO_IN_N_PIXS_OFF  (MLO_N_IN_HORIZ_PIX_READS - (MLO_N_IN_HORIZ_PIX_READS  / MLO_READ_UNIT)*MLO_READ_UNIT)

#define MLO_IN_LCL_WIDTH (MLO_N_IN_HORIZ_READS * MLO_READ_UNIT + 2 * MLO_FILTER_PAD0)
#define MLO_IN_BLK_GRP_PIX_SZ (MLO_IN_LCL_WIDTH * MLO_IN_LCL_HEIGHT)
#define MLO_IN_BLK_GRP_WK_SZ (MLO_IN_BLK_GRP_PIX_SZ/MLO_READ_UNIT)
#define MLO_IN_LCL_SZ (MLO_IN_BLK_GRP_PIX_SZ)
// LDS IN SIZE
#define MLO_TOTAL_IN_LCL_SZ (MLO_N_LCL_BATCHS*MLO_N_LCL_IN_MAPS*MLO_IN_LCL_SZ)
#define MLO_IN_VERT_READS (MLO_GRP_SZ/MLO_N_IN_HORIZ_READS)

#if (MLO_TOTAL_OUT_LCL_SZ + MLO_TOTAL_IN_LCL_SZ) > (MLO_WEI_BLKS_LCL_SZ)
#define MLO_LCL_SZ (MLO_TOTAL_OUT_LCL_SZ + MLO_TOTAL_IN_LCL_SZ)
#else
#define MLO_LCL_SZ (MLO_WEI_BLKS_LCL_SZ)
#endif

__attribute__((always_inline))
uint iDiv(uint v, uint d)
{
	uint r = (uint)((float)v * (1.0f/ (float)d) + 0.00001f);
	return(r);
}

__attribute__((always_inline))
uint iMod(uint v, uint u, uint d)
{
	uint r = v - mul24((uint)u, (uint)d);
	return(r);
}

/*********************************************************************************************************
// wrw algorithm for large filters
// idea:
// split filter taps into sub-tiles along x and y axis with number of tap groups muliples of stride or 1
// for example
// the 5x10 filter has been split into 10 sub-tiles 1x5 each, 1 tap in y direction and 5 taps in x direction.
// those horizontal taps are 0, 2, 4, 6, 8 and 1, 3, 5, 7, 9
// a single vertical tap is 0 or 1 or 2 or 3 or 4.
// one may say sub-tiles are indexed by a vertical tap.
// the partial sum has been calculated into those 10 sub-tiles in parallel.
// the full filter has been calulated by reducing all sub-tiles into a single filter per group.
// teh accumulation has been done over all pixels of several outputs being shared with a single input.
// the accuulation has been done per batch.
//
// the total reduction over all batches has been doesn a separete kerenel.
//
// alg
//
//		until end of output map (MLO_N_OUT_BLK)
//			load input map block in LDS
//			load output maps in LDS
//          for j in output scans
//				for i in output scan interval
//                  accumulate the weights into sub-tiles
//
//		reduce sub-tiles into a single filter for each output
//		write accululated weights
//
// group layout
// 0 - n waves * wave size (n_waves has been defined by host)
// 1 - input channel index
// 2 - output channel/batch index
//
//
// for each batch
//	 accumulate all weights per input/output pair

**********************************************************************************************************/

__attribute__((reqd_work_group_size(MLO_GRP_SZ0, MLO_GRP_SZ1, MLO_GRP_SZ2)))
__kernel void MIOpenCvBwdWrW(
	const __global _FLOAT * __restrict top_df,
	const __global _FLOAT * __restrict bot,
	__global _FLOAT * __restrict weights_df,
#if MLO_CONV_BIAS
	__global _FLOAT * __restrict bias_df,
#endif
	UNUSED _FLOAT padding_val
)
{

	// input/output tiles + reduce buffer

	__local _FLOAT lcl[(MLO_LCL_SZ)];
	__local _FLOAT * lcl_bot = lcl;
	__local _FLOAT * lcl_top = lcl + MLO_TOTAL_IN_LCL_SZ;

	uint c_idx_base = get_group_id(0); // input map index base

	uint o_idx_base = get_group_id(1); // output map index base
	uint ib_base = get_group_id(2); // batch index base

	uint ib = ib_base*MLO_N_LCL_BATCHS;

	uint c_idx = c_idx_base * MLO_N_LCL_IN_MAPS; // input map index

	uint o_idx = o_idx_base * (MLO_N_OUT_BLK_GRP * MLO_N_LCL_OUT_MAPS); // output map index

	uint gbl_in_off = c_idx * MLO_IN_CHANNEL_STRIDE + ib * MLO_IN_BATCH_STRIDE;
	uint gbl_out_off = o_idx * MLO_OUT_CHANNEL_STRIDE + ib * MLO_OUT_BATCH_STRIDE;

	uint lcl_id = get_local_id(0);

// weight tile id
#if MLO_WEI_BLK_SZ & (MLO_WEI_BLK_SZ - 1)
	uint w_blk_idx = iDiv(lcl_id, MLO_WEI_BLK_SZ);
	uint w_idx = iMod(lcl_id, w_blk_idx, MLO_WEI_BLK_SZ);
#else
	uint w_blk_idx = lcl_id / MLO_WEI_BLK_SZ;
	uint w_idx = lcl_id & (MLO_WEI_BLK_SZ - 1);
#endif
#if MLO_WEI_BLK_SZ0 & (MLO_WEI_BLK_SZ0 - 1)
// weight y
	uint w_y = iDiv(w_idx, MLO_WEI_BLK_SZ0);
// weight starting x
	uint w_x0 = iMod(w_idx, w_y, MLO_WEI_BLK_SZ0);
#else
	uint w_y = w_idx / MLO_WEI_BLK_SZ0;
	uint w_x0 = w_idx & (MLO_WEI_BLK_SZ0 - 1);
#endif

	__private _FLOAT pvt_accum[(MLO_N_OUT_BLK_GRP * MLO_N_LCL_OUT_MAPS * MLO_WEI_WKITEM)];

	for (uint i = 0; i < (MLO_N_OUT_BLK_GRP * MLO_N_LCL_OUT_MAPS * MLO_WEI_WKITEM); ++i)
	{
		pvt_accum[i] = 0;
	}

	// zero out LDS
	for (uint i = lcl_id; i < (MLO_LCL_SZ); i += MLO_GRP_SZ)
	{
		lcl[i] = 0;
	}

	// over all batches

	for (uint b = 0;
		b < MLO_N_BATCH_LOOPS;
		++b,
		gbl_in_off += MLO_N_LCL_BATCHS*MLO_IN_BATCH_STRIDE,
		gbl_out_off += MLO_N_LCL_BATCHS*MLO_OUT_BATCH_STRIDE
		)
	{

		barrier(CLK_LOCAL_MEM_FENCE);

		uint in_y = 0;
		uint out_y = 0;

// prefetch MLO_FILTER_STRIDE1 - MLO_FILTER_PAD1 input scans
		__private _FLOAT in_rd_data[MLO_READ_UNIT];

		uint gbl_in_scan_off = gbl_in_off;
		uint gbl_out_scan_off = gbl_out_off;
		uint c_scan = 0;

		for (uint p4 = lcl_id;  p4 < MLO_N_IN_HORIZ_READS * (MLO_FILTER_SIZE1 - MLO_FILTER_STRIDE1 - MLO_FILTER_PAD1);
			p4 += MLO_GRP_SZ)
		{
#if MLO_N_IN_HORIZ_READS & (MLO_N_IN_HORIZ_READS - 1)
			c_scan = iDiv(p4, MLO_N_IN_HORIZ_READS);
			uint c_pix4 = iMod(p4, c_scan, MLO_N_IN_HORIZ_READS);
#else
			c_scan = p4 / MLO_N_IN_HORIZ_READS;
			uint c_pix4 = p4 & (MLO_N_IN_HORIZ_READS - 1);
#endif
			for (uint i = 0; i < MLO_READ_UNIT; ++i)
			{
				in_rd_data[i] = 0;
			}
			if (in_y + c_scan < MLO_IN_HEIGHT)
			{
				uint bot_off = gbl_in_scan_off + c_scan * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT;
				const __global _FLOAT * bot_p = &bot[bot_off];
				// still problems with unaligned LDS access
#if MLO_IN_N_PIXS_OFF > 0
				if (c_pix4 == MLO_N_IN_HORIZ_READS - 1)
				{
					uint i = 0;
					for (; i < MLO_IN_N_PIXS_OFF; ++i)
					{
						in_rd_data[i] = bot_p[i];
#if DBG_OUT_OF_RNGE
						if (bot_off + i >= MLO_IN_BATCH_STRIDE * MLO_BATCH_SZ)
						{
							printf("k:err:in-of-range\n");
						}
#endif
					}

				}
				else
#endif
				{
					for (uint i = 0; i < MLO_READ_UNIT; ++i)
					{
						in_rd_data[i] = bot_p[i];
#if DBG_OUT_OF_RNGE
						if (bot_off + i >= MLO_IN_BATCH_STRIDE * MLO_BATCH_SZ)
						{
							printf("k:err:in-of-range\n");
						}
#endif
					}

				}
			}
			for (uint i = 0; i < MLO_READ_UNIT; ++i)
			{
				lcl_bot[(c_scan + MLO_FILTER_PAD1)*MLO_IN_LCL_WIDTH + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT + i] = in_rd_data[i];
			}

		}

		in_y += MLO_FILTER_SIZE1 - MLO_FILTER_STRIDE1 - MLO_FILTER_PAD1;

// TO DO: HANDLE PADDING
// over all out blocks
// processing per MLO_N_ALIGNED_OUT_SCAN_BLK output scans

		for (uint ob = 0; ob < MLO_N_OUT_BLK; ++ob, in_y += (MLO_IN_LCL_HEIGHT - MLO_FILTER_SIZE1 + MLO_FILTER_STRIDE1), out_y += MLO_N_ALIGNED_OUT_SCAN_BLK)
		{

			barrier(CLK_LOCAL_MEM_FENCE);

// fetch input: (MLO_IN_LCL_HEIGHT - MLO_FILTER_SIZE1 + 1)
// TODO:: HANDLE multiple INPUTS
// an overshoot has to be handled by zero outing output
			gbl_in_scan_off = gbl_in_off + mul24(in_y, ( uint )MLO_IN_STRIDE);
			uint c_scan2 = 0;
			for (uint p4 = lcl_id; p4 < MLO_N_IN_HORIZ_READS * (MLO_IN_LCL_HEIGHT - MLO_FILTER_SIZE1 + MLO_FILTER_STRIDE1);
				p4 += MLO_GRP_SZ)
			{
#if MLO_N_IN_HORIZ_READS & (MLO_N_IN_HORIZ_READS - 1)
				c_scan2 = iDiv(p4, MLO_N_IN_HORIZ_READS);
				uint c_pix4 = iMod(p4, c_scan2, MLO_N_IN_HORIZ_READS);
#else
				c_scan2 = p4 / MLO_N_IN_HORIZ_READS;
				uint c_pix4 = p4 & (MLO_N_IN_HORIZ_READS - 1);
#endif

				for (uint i = 0; i < MLO_READ_UNIT; ++i)
				{
					in_rd_data[i] = 0;
				}

				if (in_y + c_scan2 < MLO_IN_HEIGHT)
				{
					uint bot_off = gbl_in_scan_off + c_scan2 * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT;
					const __global _FLOAT * bot_p = &bot[bot_off];

#if MLO_IN_N_PIXS_OFF > 0
					if (c_pix4 == MLO_N_IN_HORIZ_READS - 1)
					{

						uint i = 0;
						for (; i < MLO_IN_N_PIXS_OFF; ++i)
						{
							in_rd_data[i] = bot_p[i];
#if DBG_OUT_OF_RNGE
							if (bot_off + i >= MLO_IN_BATCH_STRIDE * MLO_BATCH_SZ)
							{
								printf("k:err:in-of-range\n");
							}
#endif
						}

					}
					else
#endif
					{

						for (uint i = 0; i < MLO_READ_UNIT; ++i)
						{
							in_rd_data[i] = bot_p[i];
#if DBG_OUT_OF_RNGE
							if (bot_off + i >= MLO_IN_BATCH_STRIDE * MLO_BATCH_SZ)
							{
								printf("k:err:in-of-range\n");
							}
#endif
						}
					}

				} // if (in_y + c_scan2 < MLO_IN_HEIGHT)

				for (uint i = 0; i < MLO_READ_UNIT; ++i)
				{
					uint lcl_off = (c_scan2 + MLO_FILTER_SIZE1 - MLO_FILTER_STRIDE1)*MLO_IN_LCL_WIDTH + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT;
					lcl_bot[lcl_off + i] = in_rd_data[i];
				}

			}

			gbl_out_scan_off = gbl_out_off + mul24(out_y, ( uint )MLO_OUT_STRIDE);

// over all outputs groups
// MLO_N_OUT_BLK_GRP outputs reuse the same input
// each output blk is MLO_N_LCL_OUT_MAPS outputs
// MLO_N_LCL_OUT_MAPS nuber is restricted by LDS size

			uint gbl_out_scan_off1 = gbl_out_scan_off;
			for (uint og = 0; og < MLO_N_OUT_BLK_GRP
				; ++og, gbl_out_scan_off1 += MLO_N_LCL_OUT_MAPS*MLO_OUT_CHANNEL_STRIDE)
			{
				barrier(CLK_LOCAL_MEM_FENCE);

// fetch output. MLO_N_ALIGNED_OUT_SCAN_BLK output scans, each of size MLO_N_OUT_HORIZ_READS

				__private _FLOAT out_rd_data[MLO_READ_UNIT];

				for (uint oo_p4 = lcl_id; oo_p4 < (MLO_N_LCL_OUT_MAPS*MLO_N_ALIGNED_OUT_SCAN_BLK*MLO_N_OUT_HORIZ_READS);
																											oo_p4 += MLO_GRP_SZ)
				{
#if (MLO_N_ALIGNED_OUT_SCAN_BLK*MLO_N_OUT_HORIZ_READS) & ((MLO_N_ALIGNED_OUT_SCAN_BLK*MLO_N_OUT_HORIZ_READS) - 1)
					uint o = iDiv(oo_p4, (MLO_N_ALIGNED_OUT_SCAN_BLK*MLO_N_OUT_HORIZ_READS));
					uint o_pX4 = iMod(oo_p4, o, (MLO_N_ALIGNED_OUT_SCAN_BLK*MLO_N_OUT_HORIZ_READS));
#else
					uint o = oo_p4 / (MLO_N_ALIGNED_OUT_SCAN_BLK*MLO_N_OUT_HORIZ_READS);
					uint o_pX4 = oo_p4 & (MLO_N_ALIGNED_OUT_SCAN_BLK*MLO_N_OUT_HORIZ_READS) - 1);
#endif
#if MLO_N_OUT_HORIZ_READS & (MLO_N_OUT_HORIZ_READS - 1)
					uint o_scan = iDiv(o_pX4, MLO_N_OUT_HORIZ_READS);
					uint o_pix4 = iMod(o_pX4, o_scan, MLO_N_OUT_HORIZ_READS);
#else
					uint o_scan = o_pX4 / MLO_N_OUT_HORIZ_READS;
					uint o_pix4 = o_pX4 & (MLO_N_OUT_HORIZ_READS - 1);
#endif
					for (uint i = 0; i < MLO_READ_UNIT; ++i)
					{
						out_rd_data[i] = 0;
					}
					uint top_df_off = gbl_out_scan_off1 + o*MLO_OUT_CHANNEL_STRIDE + o_scan * MLO_OUT_STRIDE + o_pix4*MLO_READ_UNIT;
					const __global _FLOAT * top_df_p = &top_df[top_df_off];

					if (out_y + o_scan < MLO_OUT_HEIGHT
						&& o_idx + og *MLO_N_LCL_OUT_MAPS + o < MLO_N_OUTPUTS
						)
					{
// scan has been fetch by 4
// here the non-multiple of 4 scan has been handled
// also makes sure the input garbage hs been multipled by 0
#if MLO_OUT_N_PIXS_OFF > 0
						if (o_pix4 == (MLO_N_OUT_HORIZ_READS - 1))
						{
							uint i = 0;
							for (; i < MLO_OUT_N_PIXS_OFF; ++i)
							{
								out_rd_data[i] = top_df_p[i];
#if DBG_OUT_OF_RNGE
								if (top_df_off + i >= MLO_OUT_BATCH_STRIDE * MLO_BATCH_SZ)
								{
									printf("k:err:out-of-range\n");
								}
#endif
							}
						}

						else
#endif

						{
							for (uint i = 0; i < MLO_READ_UNIT; ++i)
							{
								out_rd_data[i] = top_df_p[i];
#if DBG_OUT_OF_RNGE
								if (top_df_off + i >= MLO_OUT_BATCH_STRIDE * MLO_BATCH_SZ)
								{
									printf("k:err:out-of-range\n");
								}
#endif
							}
						}

					} // if (out_y + o_scan < MLO_OUT_HEIGHT && o_idx + og *MLO_N_LCL_OUT_MAPS + o < MLO_N_OUTPUTS)

// write into LDS with MLO_OUT_HORIZ_PIX_EXT_SZ stride to zero out weights block overshoot

					for (uint i = 0; i < MLO_READ_UNIT; ++i)
					{
						uint lcl_off = o * MLO_OUT_LCL_SZ + o_scan * MLO_OUT_HORIZ_PIX_EXT_SZ + o_pix4*MLO_READ_UNIT;
						lcl_top[lcl_off + i] = out_rd_data[i];
					}

				} //	for (uint oo_p4 = lcl_id; oo_p4 < (MLO_N_LCL_OUT_MAPS*MLO_N_ALIGNED_OUT_SCAN_BLK*MLO_N_OUT_HORIZ_READS); oo_p4 += MLO_GRP_SZ)

				barrier(CLK_LOCAL_MEM_FENCE);

// process
// algorithm

					// over all input scans in LDS
					for (uint j = 0; j < MLO_N_ALIGNED_OUT_SCAN_BLK; ++j)
					{

						// prefetch proper inputs pixels.
						// they are MLO_WEI_BLK_SZ0 apart taps of the filter

						_FLOAT i_vals[MLO_WEI_WKITEM];

						for (uint w = 0; w < (MLO_WEI_WKITEM - (MLO_FILTER_STRIDE0 / MLO_WEI_BLK_SZ0)); ++w)
						{
							uint w_x = w_x0 + w*MLO_WEI_BLK_SZ0;
							uint i_off = (j*MLO_FILTER_STRIDE1 + w_y) * MLO_IN_LCL_WIDTH + (w_blk_idx*MLO_OUT_WEI_SCAN_BLK + 0) * MLO_FILTER_STRIDE0 + w_x;
							_FLOAT i_val = lcl_bot[i_off];

							i_vals[w] = i_val;

						}

						// if we overshoot the scanline
						// out data will be 0 by initial setting
						for (uint i = 0; i < MLO_OUT_WEI_SCAN_BLK; ++i)
						{

							// read the current input pixel
							for (uint w = (MLO_WEI_WKITEM - (MLO_FILTER_STRIDE0 / MLO_WEI_BLK_SZ0)); w < MLO_WEI_WKITEM; ++w)
							{
								uint w_x = w_x0 + w* MLO_WEI_BLK_SZ0;
								uint i_off = (j*MLO_FILTER_STRIDE1 + w_y) * MLO_IN_LCL_WIDTH + (w_blk_idx*MLO_OUT_WEI_SCAN_BLK + i) * MLO_FILTER_STRIDE0 + w_x;
								_FLOAT i_val = lcl_bot[i_off];

								i_vals[w] = i_val;
							}
							// for each output accumulate a proper filter tap
							for (uint o = 0; o < MLO_N_LCL_OUT_MAPS; ++o)
							{

								// read with MLO_OUT_HORIX_PIX_EXT_SZ stride
								_FLOAT o_val
									= lcl_top[(o)*MLO_OUT_LCL_SZ + j * MLO_OUT_HORIZ_PIX_EXT_SZ + (w_blk_idx*MLO_OUT_WEI_SCAN_BLK + i)];

								uint w = 0;
								_FLOAT i_val;
								for (/*uint w = 0*/; w < MLO_WEI_WKITEM; ++w)
								{

									i_val = i_vals[w];

									pvt_accum[(og * MLO_N_LCL_OUT_MAPS + o) * MLO_WEI_WKITEM + w] += i_val * o_val;

								} // for (/*uint w = 0*/; w < MLO_WEI_WKITEM; ++w)

							} // for (uint o = 0; o < MLO_N_LCL_OUT_MAPS; ++o)

							for (uint w = 0; w < (MLO_WEI_WKITEM - (MLO_FILTER_STRIDE0 / MLO_WEI_BLK_SZ0)); ++w)
							{
								i_vals[w] = i_vals[w + (MLO_FILTER_STRIDE0 / MLO_WEI_BLK_SZ0)];

							}
						} // for (uint i = 0; i < MLO_OUT_WEI_SCAN_BLK; ++i)
					} // for (uint j = 0; j < MLO_N_ALIGNED_OUT_SCAN_BLK; ++j)

			} // for(; og < (MLO_N_OUT_BLK_GRP; ++og )

// move the input data tail inside LDS to reduce mem bandwidth
			for (uint c_scan3 = 0; c_scan3 < (MLO_FILTER_SIZE1 - MLO_FILTER_STRIDE1); ++c_scan3)
			{
				barrier(CLK_LOCAL_MEM_FENCE);

				for (uint p4 = lcl_id; p4 < MLO_N_IN_HORIZ_READS;
												p4 += MLO_GRP_SZ)
				{

					uint c_pix4 = p4;
					for (uint i = 0; i < MLO_READ_UNIT; ++i)
					{
						lcl_bot[c_scan3*(MLO_IN_LCL_WIDTH)+MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT + i]
							= lcl_bot[(c_scan3 + (MLO_IN_LCL_HEIGHT - MLO_FILTER_SIZE1 + MLO_FILTER_STRIDE1))*(MLO_IN_LCL_WIDTH)+MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT + i];
					}

				}
			}

		} // for (uint ob = 0; ob < MLO_N_OUT_BLK; ++ob, in_y += (MLO_IN_LCL_HEIGHT - MLO_FILTER_SIZE1 + 1), out_y += MLO_N_ALIGNED_OUT_SCAN_BLK)
	} // for (uint b = 0;

// send it out

	uint wei_lcl_off = 0;
	_FLOAT final_sum = 0;

// save in lcl and orgnize in a proper order
	// outputs
	//	filter size1
	//	  filter size0

// TO DO:: DEPENDING ON THE GROUP SIZE
	for (uint og = 0; og < MLO_N_OUT_BLK_GRP; ++og)
	{
		barrier(CLK_LOCAL_MEM_FENCE);
		if ( w_blk_idx < MLO_MAX_WEI_BLK_LOOP)
		{
			for (uint o = 0; o < MLO_N_LCL_OUT_MAPS; ++o)
			{
				uint w = 0;
				for (; w < MLO_WEI_WKITEM; ++w)
				{
					// save "virtual" filter table
					uint w_x = w_x0 + w*MLO_WEI_BLK_SZ0;
					wei_lcl_off = ((o * MLO_MAX_WEI_BLK_LOOP + w_blk_idx) * MLO_FILTER_SIZE1 + w_y) * (MLO_WEI_BLK_SZ0 *MLO_WEI_WKITEM) + w_x;
					lcl[wei_lcl_off] = pvt_accum[(og * MLO_N_LCL_OUT_MAPS + o) * MLO_WEI_WKITEM + w];

				}

			}
		}

		barrier(CLK_LOCAL_MEM_FENCE);

		// read into real filter table

		for(uint l = lcl_id; l < (MLO_N_LCL_OUT_MAPS *MLO_WEI_CHANNEL_STRIDE); l+=MLO_GRP_SZ)
		{
#if MLO_WEI_CHANNEL_STRIDE & (MLO_WEI_CHANNEL_STRIDE - 1)
			uint oo = iDiv(l, MLO_WEI_CHANNEL_STRIDE);
			uint wei_i = iMod(l, oo, MLO_WEI_CHANNEL_STRIDE);
#else
			uint oo = l / MLO_WEI_CHANNEL_STRIDE;
			uint wei_i = l & MLO_WEI_CHANNEL_STRIDE - 1);
#endif
#if (MLO_FILTER_SIZE0) & ((MLO_FILTER_SIZE0) - 1)
			uint wei_i_y = iDiv(wei_i, (MLO_FILTER_SIZE0));
			uint wei_i_x = iMod(wei_i, wei_i_y, (MLO_FILTER_SIZE0));
#else
			uint wei_i_y = wei_i / (MLO_FILTER_SIZE0);
			uint wei_i_x = wei_i & ((MLO_FILTER_SIZE0) - 1);
#endif
			// send it out
			// inputs are outputs
			uint wei_df_off = ((ib * MLO_N_OUTPUTS + o_idx) * (uint)MLO_WEI_BATCH_STRIDE)
				// this input channel
				+ mul24(c_idx, (uint)MLO_WEI_CHANNEL_STRIDE);

			final_sum = 0;
			for (uint i = 0; i < MLO_MAX_WEI_BLK_LOOP; ++i)
			{
				final_sum += lcl_bot[((oo * MLO_MAX_WEI_BLK_LOOP + i) * MLO_FILTER_SIZE1 + wei_i_y) * (MLO_WEI_BLK_SZ0 *MLO_WEI_WKITEM) + wei_i_x];
			}

			weights_df[wei_df_off + (og *  MLO_N_LCL_OUT_MAPS + oo) * MLO_WEI_BATCH_STRIDE + wei_i] = final_sum; //lcl_bot[lcl_id]; //
		}

	} // for(uint og = 0; og < MLO_N_OUT_BLK_GRP; ++og)

}

// final reduction kernel
// add filters over batches
__attribute__((reqd_work_group_size(MLO_UT_GRP_SZ0, 1, 1)))
__kernel void MIOpenCvBwdWrW_rdc(
	const __global _FLOAT * __restrict weight_df_tmp,
	__global _FLOAT * __restrict weights_df
)
{
	uint gbl_id = get_global_id(0);
	uint wei_idx0 = gbl_id * MLO_UT_READ_UNIT;

#if MLO_WEI_CHANNEL_STRIDE & (MLO_WEI_CHANNEL_STRIDE - 1)
	uint wei_blk_idx = iDiv(wei_idx0, MLO_WEI_CHANNEL_STRIDE);
	uint wei_idx = iMod(wei_idx0, wei_blk_idx, MLO_WEI_CHANNEL_STRIDE);
#else
	uint wei_blk_idx = wei_idx0 / MLO_WEI_CHANNEL_STRIDE;
	uint wei_idx = wei_idx0 & (MLO_WEI_CHANNEL_STRIDE - 1);
#endif

	_FLOAT pvt_accum_wei[MLO_UT_READ_UNIT];
	for (uint i = 0; i < MLO_UT_READ_UNIT; ++i)
	{
		pvt_accum_wei[i] = 0;
	}

	uint batch_loop = (MLO_BATCH_SZ + (MLO_N_BATCH_LOOPS*MLO_N_LCL_BATCHS) - 1) / (MLO_N_BATCH_LOOPS*MLO_N_LCL_BATCHS);
	for (uint i = 0; i < batch_loop; ++i)
	{
		for (uint j = 0; j < MLO_UT_READ_UNIT; ++j)
		{
			pvt_accum_wei[j]
				+= weight_df_tmp[(wei_blk_idx * MLO_WEI_CHANNEL_STRIDE + i* MLO_N_OUTPUTS*MLO_WEI_BATCH_STRIDE) + wei_idx + j];
		}
	}

	for (uint j = 0; j < MLO_UT_READ_UNIT; ++j)
	{
		weights_df[wei_idx0 + j] = pvt_accum_wei[j];
	}

}


Add boundary condition management to sim_fluid

#include "std_helpers.clh"
#include "fluid_defs.clh"
#include "solve_poisson.clh"

//
// Symbol table
// ·∇, ∇²
//

//
// du
// ── = -(u·∇)u - (1/ϱ)∇p + ν·∇²u(x, t) + F(t)
// dt
//
// w = -(u·∇)u + ν·∇²u(x, t) + F(t)
//
// w - (1/ϱ)∇p = du/dt
//

struct VoxelGridDescription {
    float3 origin_m;
    float spacing_m;
};

__kernel void apply_velocity_bdry_cond(
    __read_only image3d_t u0,
    __write_only image3d_t u1) {

    const int4 vxl_coord = (int4) (get_global_id(0), get_global_id(1), get_global_id(2), 0);

    // This is terrible, I know. We'll make it faster once it's stable.
    const sampler_t smp_volume_nearest =
        CLK_NORMALIZED_COORDS_FALSE |
        CLK_ADDRESS_CLAMP |
        CLK_FILTER_NEAREST;

    const int4 unit_x = (int4) (1, 0, 0, 0);
    const int4 unit_y = (int4) (0, 1, 0, 0);
    const int4 unit_z = (int4) (0, 0, 1, 0);
    const float3 u_inner_bdry = read_imagef(u0, smp_volume_nearest, vxl_coord).xyz;
    if (vxl_coord.x == 99) {
        write_imagef(u1, vxl_coord + unit_x, (float4) (-u_inner_bdry, 0.0f));
    }
    if (vxl_coord.x == 1) {
        write_imagef(u1, vxl_coord - unit_x, (float4) (-u_inner_bdry, 0.0f));
    }
    if (vxl_coord.y == 99) {
        write_imagef(u1, vxl_coord + unit_y, (float4) (-u_inner_bdry, 0.0f));
    }
    if (vxl_coord.y == 1) {
        write_imagef(u1, vxl_coord - unit_y, (float4) (-u_inner_bdry, 0.0f));
    }
    if (vxl_coord.z == 99) {
        write_imagef(u1, vxl_coord + unit_z, (float4) (-u_inner_bdry, 0.0f));
    }
    if (vxl_coord.z == 1) {
        write_imagef(u1, vxl_coord - unit_z, (float4) (-u_inner_bdry, 0.0f));
    }
    write_imagef(u1, vxl_coord, (float4) (u_inner_bdry, 0.0f));
}

__kernel void advect_velocity(
    __read_only image3d_t vol_u_0,
    __write_only image3d_t vol_u_1,
    struct FluidSimConfig cfg) {
    //
    // u0 = vol_u_0[x]
    // vol_u_1[x] = vol_u_0[x - (u0 * dt_sec)]
    //
    const sampler_t smp_volume_interp =
        CLK_NORMALIZED_COORDS_FALSE |
        CLK_ADDRESS_CLAMP_TO_EDGE |
        CLK_FILTER_LINEAR;

    const sampler_t smp_volume_nearest =
        CLK_NORMALIZED_COORDS_FALSE |
        CLK_ADDRESS_CLAMP |
        CLK_FILTER_NEAREST;

    const int4 vxl_coordi = (int4) (get_global_id(0), get_global_id(1), get_global_id(2), 0);
    const float3 u0 = read_imagef(vol_u_0, smp_volume_nearest, vxl_coordi).xyz;

    const float3 vxl_coordf = convert_float4(vxl_coordi).xyz;
    const float3 u0_real_coord = (vxl_coordf / cfg.dx_m);

    const float3 u1_real_coord = u0_real_coord - (u0 * cfg.dt_sec);
    const float3 u1_vxl_coordf = u1_real_coord * cfg.dx_m;
    const int4 u1_vxl_coordi = (int4) (convert_int3(u1_vxl_coordf), 0);
    const float4 u1 = read_imagef(vol_u_0, smp_volume_interp, u1_vxl_coordi);

    float4 force = (float4) (0.0f, 0.0f, 0.0f, 0.0f);
    if ((vxl_coordi.x > 1) &&
        (vxl_coordi.x < 10) &&
        (vxl_coordi.y > 1) &&
        (vxl_coordi.y < 10) &&
        (vxl_coordi.z > 1) &&
        (vxl_coordi.z < 10)) {
        force.x = -10.1;
        force.y = -10.1;
        force.z = -10.1;
    }
    float4 u1_damped = u1 + (force * cfg.dt_sec);

    const int4 unit_x = (int4) (1, 0, 0, 0);
    const int4 unit_y = (int4) (0, 1, 0, 0);
    const int4 unit_z = (int4) (0, 0, 1, 0);

    write_imagef(vol_u_1, vxl_coordi, u1_damped);
}

/*
__kernel void advect_scalar(
    __read_only image3d_t vol_u_0,
    __read_only image3d_t vol_q_0,
    __write_only image3d_t vol_q_1,
    struct FluidSimConfig cfg) {
    //
    // u0 = vol_u_0[x]
    // vol_u_1[x] = vol_u_0[x - (u0 * dt_sec)]
    //
    const sampler_t smp_volume = CLK_NORMALIZED_COORDS_FALSE |
                                 CLK_ADDRESS_CLAMP_TO_EDGE |
                                 CLK_FILTER_LINEAR;

    const int4 vxl_coordi = (int4) (get_global_id(0), get_global_id(1), get_global_id(2), 0);
    const float3 u0 = read_imagef(vol_u_0, smp, vxl_coordi).xyz;

    const float3 vxl_coordf = convert_float4(vxl_coordi).xyz;
    const float3 u0_real_coord = (vxl_coordi / cfg.dx_m);

    const float3 write_real_coord = u0_real_coord - (u0 * cfg.dt_sec);
    const float3 write_vxl_coordf = write_real_coord * cfg.dx_m;
    const int4 write_vxl_coordi = (int4) (convert_int3(write_vxl_coordf), 0);

    write_image(vol_u_1, write_vxl_coordi, u_0);
}*/

// Viscous Diffusion
//
// du
// ── = ν·∇²u(x, t) =
// dt

__kernel void diffuse(
    __read_only image3d_t vol_u_0,
    __read_only image3d_t vol_u_next1,
    __write_only image3d_t vol_u_next2,
    struct FluidSimConfig cfg) {
    const int4 vxl_coord = (int4) (get_global_id(0), get_global_id(1), get_global_id(2), 0);

    const float alpha = (cfg.dx_m * cfg.dx_m) / (cfg.nu * cfg.dt_sec);
    const float beta = 4.0f + alpha;

    const float4 jacobi_iterate = poisson_step_3d_vector(
        vol_u_next1,
        vol_u_0,
        vxl_coord,
        alpha,
        beta
    );

    write_imagef(vol_u_next2, vxl_coord, jacobi_iterate);
}

__kernel void compute_divergence(
    __read_only image3d_t vol_u,
    __write_only image3d_t vol_out,
    struct FluidSimConfig cfg) {
    const int4 vxl_coord = (int4) (get_global_id(0), get_global_id(1), get_global_id(2), 0);

    const sampler_t smp =
        CLK_NORMALIZED_COORDS_FALSE |
        CLK_ADDRESS_CLAMP_TO_EDGE |
        CLK_FILTER_NEAREST;

    const int4 unit_x = (int4) (1, 0, 0, 0);
    const int4 unit_y = (int4) (0, 1, 0, 0);
    const int4 unit_z = (int4) (0, 0, 1, 0);

    // +/- x
    const float xk_nx = read_imagef(vol_u, smp, (vxl_coord - unit_x)).x;
    const float xk_px = read_imagef(vol_u, smp, (vxl_coord + unit_x)).x;

    // +/- y
    const float xk_ny = read_imagef(vol_u, smp, (vxl_coord - unit_y)).y;
    const float xk_py = read_imagef(vol_u, smp, (vxl_coord + unit_y)).y;

    // +/- z
    const float xk_nz = read_imagef(vol_u, smp, (vxl_coord - unit_z)).z;
    const float xk_pz = read_imagef(vol_u, smp, (vxl_coord + unit_z)).z;

    const float inv_2dx = 0.5f / (cfg.dx_m * cfg.dx_m);
    float div_u = (xk_pz - xk_nz) * inv_2dx;
    div_u += (xk_py - xk_ny) * inv_2dx;
    div_u += (xk_px - xk_nx) * inv_2dx;

    float4 div = (float4) (div_u, div_u, div_u, 0.0f);
    write_imagef(vol_out, vxl_coord, (float4) div_u);
}

__kernel void apply_pressure_bdry_cond(
    __read_only image3d_t p0,
    __write_only image3d_t p1) {
    const int4 vxl_coord = (int4) (get_global_id(0), get_global_id(1), get_global_id(2), 0);

    const sampler_t smp_volume_nearest =
        CLK_NORMALIZED_COORDS_FALSE |
        CLK_ADDRESS_CLAMP |
        CLK_FILTER_NEAREST;

    // This is terrible, I know. We'll make it faster once it's stable.

    const int4 unit_x = (int4) (1, 0, 0, 0);
    const int4 unit_y = (int4) (0, 1, 0, 0);
    const int4 unit_z = (int4) (0, 0, 1, 0);
    const float4 p_inner_bdry = read_imagef(p0, smp_volume_nearest, vxl_coord);
    if (vxl_coord.x == 99) {
        write_imagef(p1, vxl_coord + unit_x, p_inner_bdry);
    }
    if (vxl_coord.x == 1) {
        write_imagef(p1, vxl_coord - unit_x, p_inner_bdry);
    }
    if (vxl_coord.y == 99) {
        write_imagef(p1, vxl_coord + unit_y, p_inner_bdry);
    }
    if (vxl_coord.y == 1) {
        write_imagef(p1, vxl_coord - unit_y, p_inner_bdry);
    }
    if (vxl_coord.z == 99) {
        write_imagef(p1, vxl_coord + unit_z, p_inner_bdry);
    }
    if (vxl_coord.z == 1) {
        write_imagef(p1, vxl_coord - unit_z, p_inner_bdry);
    }
    write_imagef(p1, vxl_coord, p_inner_bdry);
}
// du
// ── = ∇·u(x, t)
// dt
//
// w = (u + ∇p)
// ∇²p = ∇·w
__kernel void compute_pressure(
    __read_only image3d_t div_w,
    __read_only image3d_t vol_p0,
    __write_only image3d_t vol_p1,
    struct FluidSimConfig cfg) {
    const int4 vxl_coord = (int4) (get_global_id(0), get_global_id(1), get_global_id(2), 0);

    const float alpha = -(cfg.dx_m * cfg.dx_m);
    const float beta = 4.0f;

    const float jacobi_iterate = poisson_step_3d_scalar(
        vol_p0,
        div_w,
        vxl_coord,
        alpha,
        beta
    );
    write_imagef(vol_p1, vxl_coord, (float4) jacobi_iterate);
}

// Eliminate divergence
__kernel void chiron_projection(
    __read_only image3d_t u_divergent,
    __read_only image3d_t vol_p,
    __write_only image3d_t vol_u_1,
    struct FluidSimConfig cfg) {
    const int4 vxl_coord = (int4) (get_global_id(0), get_global_id(1), get_global_id(2), 0);

    const sampler_t smp =
        CLK_NORMALIZED_COORDS_FALSE |
        CLK_ADDRESS_CLAMP_TO_EDGE |
        CLK_FILTER_NEAREST;

    const float3 w = read_imagef(u_divergent, smp, vxl_coord).xyz;

    const int4 unit_x = (int4) (1, 0, 0, 0);
    const int4 unit_y = (int4) (0, 1, 0, 0);
    const int4 unit_z = (int4) (0, 0, 1, 0);

    // Is this just writing the first element and not all elements?
    const float p0x = read_imagef(vol_p, smp, vxl_coord).x;
    const float3 p000 = (float3) (p0x, p0x, p0x);
    const float p100 = read_imagef(vol_p, smp, vxl_coord + unit_x).x;
    const float p010 = read_imagef(vol_p, smp, vxl_coord + unit_y).x;
    const float p001 = read_imagef(vol_p, smp, vxl_coord + unit_z).x;

    const float3 p_smp = (float3) (p100, p010, p001);
    const float3 dp_dx = (p_smp - p000) / cfg.dx_m;

    const float3 u1 = w - dp_dx;

    write_imagef(vol_u_1, vxl_coord, (float4) (u1, 0.0f));

}

#include "std_helpers.clh"

#include "fluid_types.clh"

//
// Symbol table
// ·∇, ∇²
//

struct FluidSimConfig {
    float dt_sec;
    float dx_m;
    float nu;
};

struct VoxelGridDescription {
    float3 origin_m;
    float spacing_m;
};

__kernel void advect_velocity(
    __read_only cl::Image3D vol_u_0,
    __write_only cl::Image3D vol_u_1,
    struct FluidSimConfig cfg) {
    //
    // u0 = vol_u_0[x]
    // vol_u_1[x] = vol_u_0[x - (u0 * dt_sec)]
    //
    const sampler_t smp_volume = CLK_NORMALIZED_COORDS_FALSE |
                                 CLK_ADDRESS_CLAMP_TO_EDGE |
                                 CLK_FILTER_LINEAR;
    const int4 vxl_coordi = (int4) (get_global_id(0), get_global_id(1), get_global_id(2), 0);
    const float3 u0 = read_imagef(vol_u_0, smp, vxl_coordi).xyz;

    const float3 vxl_coordf = convert_float4(vxl_coordi).xyz;
    const float3 u0_real_coord = (vxl_coordi / cfg.dx_m);

    const float3 write_real_coord = u0_real_coord - (u0 * cfg.dt_sec);
    const float3 write_vxl_coordf = write_real_coord * cfg.dx_m;
    const int4 write_vxl_coordi = (int4) (convert_int3(write_vxl_coordf), 0);

    write_image(vol_u_1, write_vxl_coordi, u_0);
}

/*
__kernel void advect_scalar(
    __read_only cl::Image3D vol_u_0,
    __read_only cl::Image3D vol_q_0,
    __write_only cl::Image3D vol_q_1,
    struct FluidSimConfig cfg) {
    //
    // u0 = vol_u_0[x]
    // vol_u_1[x] = vol_u_0[x - (u0 * dt_sec)]
    //
    const sampler_t smp_volume = CLK_NORMALIZED_COORDS_FALSE |
                                 CLK_ADDRESS_CLAMP_TO_EDGE |
                                 CLK_FILTER_LINEAR;

    const int4 vxl_coordi = (int4) (get_global_id(0), get_global_id(1), get_global_id(2), 0);
    const float3 u0 = read_imagef(vol_u_0, smp, vxl_coordi).xyz;

    const float3 vxl_coordf = convert_float4(vxl_coordi).xyz;
    const float3 u0_real_coord = (vxl_coordi / cfg.dx_m);

    const float3 write_real_coord = u0_real_coord - (u0 * cfg.dt_sec);
    const float3 write_vxl_coordf = write_real_coord * cfg.dx_m;
    const int4 write_vxl_coordi = (int4) (convert_int3(write_vxl_coordf), 0);

    write_image(vol_u_1, write_vxl_coordi, u_0);
}*/

//
//
// du
// ── = -(u·∇)u - (1/ϱ)∇p + ν·∇²u(x, t) + F(t)
// dt
//

// Viscous Diffusion
// ν·∇²u(x, t)
//
// du
// ── = ∇·u(x, t)
// dt
//
// ν·∇²p(x, t) = ∇·u(x, t)
//
__kernel void diffuse(
    __read_only cl::Image3D vol_u_0,
    __write_only cl::Image3D vol_u_1,
    struct FluidSimConfig cfg) {

    const float alpha = cfg.dx_m * cfg.dx_m / cfg.dt_sec
    // const float beta = nu *
}

//
// ν·∇²p(x, t) = ∇·u(x, t)
//
__kernel void compute_pressure(
    __read_only cl::Image3D vol_u_0,
    __write_only cl::Image3D vol_p,
    struct FluidSimConfig cfg) {
}

// Eliminate divergence
__kernel void chiron_projection(
    __read_only cl::Image3D vol_u_0,
    __read_only cl::Image3D vol_p,
    __write_only cl::Image3D vol_u_1,
    struct FluidSimConfig cfg) {
}


Tabbed alignment fix

Tabbed alignment fix

/* Deathray - An Avisynth plug-in filter for spatial/temporal non-local means de-noising.
 *
 * version 1.01
 *
 * Copyright 2013, Jawed Ashraf - Deathray@cupidity.f9.co.uk
 */

__attribute__((reqd_work_group_size(8, 32, 1)))
__kernel void NLMMultiFrameFourPixel(
	read_only 	image2d_t 	target_plane,			// plane being filtered
	read_only 	image2d_t 	sample_plane,			// any other plane
	const		int			sample_equals_target,	// 1 when sample plane is target plane, 0 otherwise
	const		int			width,					// width in pixels
	const		int			height,					// height in pixels
	const		float		h,						// strength of denoising
	const		int			sample_expand,			// factor to expand sample radius
	constant	float		*g_gaussian,			// 49 weights of guassian kernel
	const		int			intermediate_width,		// width, in float4s, of intermediate buffers
	const		int			linear,					// process plane in linear space instead of gamma space
	global 		float4		*intermediate_average,	// intermediate average for 4 pixels
	global 		float4		*intermediate_weight) {	// intermediate weight for 4 pixels

	// Each work group produces 1024 filtered pixels, organised as a tile
	// of 32x32, for a single iteration of multi-pass filtering. Each
	// iteration computes average and weight for each pixel based upon
	// the target plane and one other plane. After all passes have
	// completed, another kernel is used to compute the final average
	// for each pixel based upon the intermediate average and weights
	// cumulatively produced by each pass.
	//
	// Each work item computes 4 pixels in a contiguous horizontal strip.
	//
	// The tile is bordered with an apron that's 8 pixels wide. This apron
	// consists of pixels from adjoining tiles, where available. If the
	// tile is at the frame edge, the apron is filled with pixels mirrored
	// from just inside the frame.
	//
	// Input plane contains pixels as uchars. UNORM8 format is defined,
	// so a read converts uchar into a normalised float of range 0.f to 1.f.
	//
	// Destination is a pair of float4 formatted buffers for average
	// (weighted running sum) and weight (running sum of weights).

	__local float tile[TILE_SIDE * TILE_SIDE];

	int2 local_id;
	int2 source;
	Coordinates32x32(&local_id, &source);

	// Inside local memory the top-left corner of the tile is at (8,8)
	int2 target = (int2)((local_id.x << 2) + 8, local_id.y + 8);

	// The tile is 48x48 pixels which is entirely filled from the source
	FetchAndMirror48x48(target_plane, width, height, local_id, source, linear, tile) ;

	// Populate the 10x7 target window from the tile
	int kernel_radius = 3;
	float16 target_window[7];
	for (int y = 0; y < 2 * kernel_radius + 1; ++y) {
		target_window[y] = ReadTile16(target.x - kernel_radius,
									  target.y + y - kernel_radius,
									  tile);
	}

	// Most planes are planes other than the target plane, which need
	// to be fetched into the tile for sampling
	if (!sample_equals_target)
		FetchAndMirror48x48(sample_plane, width, height, local_id, source, linear, tile);

	int linear_address = source.y * intermediate_width + source.x;
	float4 average = intermediate_average[linear_address];
	float4 weight = intermediate_weight[linear_address];

	Filter4(target, h, sample_expand, target_window, tile, g_gaussian, sample_equals_target, &average, &weight);

	if (target.y < height) {
		intermediate_average[linear_address] = average;
		intermediate_weight[linear_address] = weight;
	}
}

__attribute__((reqd_work_group_size(8, 32, 1)))
__kernel void NLMFinalise(
	read_only 				image2d_t 	target_plane,			// plane being filtered
	const		global 		float4		*intermediate_average,	// final average for 4 pixels
	const		global 		float4		*intermediate_weight,	// final weight for 4 pixels
	const					int			intermediate_width,		// width, in float4s, of intermediate buffers
	const					int			linear,					// process plane in linear space instead of gamma space
	write_only 				image2d_t 	destination_plane) {	// final result

	// Computes the final pixel value based upon the average and weight
	// values for each pixel generated by multiple filtering passes.
	//
	// Each work item computes 4 pixels in the result.
	//
	// Destination plane is formatted as UNORM8 uchar. The device
	// automatically converts a pixel in range 0.f to 1.f into 0 to 255.

	int2 local_id;
	int2 destination;
	Coordinates32x32(&local_id, &destination);

	int linear_address = destination.y * intermediate_width + destination.x;

	float4 average = intermediate_average[linear_address];
	float4 weight = intermediate_weight[linear_address];

	float4 filtered_pixels = average / weight;

#if 1
	const sampler_t plane = CLK_NORMALIZED_COORDS_FALSE |
							CLK_ADDRESS_CLAMP |
							CLK_FILTER_NEAREST;

	float4 original = read_imagef(target_plane, plane, destination);

	float4 difference = filtered_pixels - original;
	float4 correction = (difference * original * original) -
					    ((difference * original) * (difference * original));

	filtered_pixels = filtered_pixels - correction;
#endif
	WritePixel4(filtered_pixels, destination, linear, destination_plane);
}